简体   繁体   中英

Java 1.6 : Learn how to handle exceptions

I did extensive research on exceptions, but I'm still lost. I'd like to know what is good to do or not.

And I'd also like you to give me your expert opinion on the following example :

public void myprocess(...) {
    boolean error  = false;

    try {

        // Step 1
        try {
            startProcess();
        } catch (IOException e) {
            log.error("step1", e);
            throw new MyProcessException("Step1", e);
        }

        // Step 2
        try {
            ...
        } catch (IOException e) {
            log.error("step2", e);
            throw new MyProcessException("Step2", e);
        } catch (DataAccessException e) {
            log.error("step2", e);
            throw new MyProcessException("Step2", e);       
        }

        // Step 3
        try {
            ...
        } catch (IOException e) {
            log.error("step3", e);
            throw new MyProcessException("Step3", e);
        } catch (ClassNotFoundException e) {
            log.error("step3", e);
            throw new MyProcessException("Step3", e);       
        }

        // etc.

    } catch (MyProcessException mpe) {
        error = true;       
    } finally {
        finalizeProcess(error);
        if (!error) {
            log.info("OK");
        } else {
            log.info("NOK");
        }           
    }
}
  1. Is it ok to throw a personnal exception (MyProcessException) in each step in order to manage a global try...catch...finally ?
  2. Is it ok to manage each known exception for each step ?

Thank you for your help.


EDIT 1 :

Is it a good practice like this ? log directly in global catch by getting message, and try...catch(Exception) in upper level....

The purpose is to stop if a step fail, and to finalize the process (error or not).

In Controller

public void callProcess() {
    try {
        myprocess(...);
    } catch (Exception e) {
        log.error("Unknown error", e);
    }   
}

In Service

public void myprocess(...) {
    boolean error  = false;

    try {

        // Step 1
        try {
            startProcess();
            log.info("ok");
        } catch (IOException e) {
            throw new MyProcessException("Step1", e);
        }

        // Step 2
        try {
            ...
        } catch (IOException e) {
            throw new MyProcessException("Step2", e);
        } catch (DataAccessException e) {
            throw new MyProcessException("Step2", e);       
        }

        // Step 3
        try {
            ...
        } catch (IOException e) {
            throw new MyProcessException("Step3", e);
        } catch (ClassNotFoundException e) {
            throw new MyProcessException("Step3", e);       
        }

        // etc.

    } catch (MyProcessException mpe) {
        error = true;       
        log.error(mpe.getMessage(), mpe);           
    } finally {
        finalizeProcess(error);
        if (!error) {
            log.info("OK");
        } else {
            log.info("NOK");
        }           
    }
}

Thank you.


Edit 2 :

Is it a real bad practice to catch (Exception e) in lower level and to throws a personnal exception ?

In your example above it may well be acceptable to throw your own custom exception.

Imagine I have some data access objects (DAO) which come in different flavours (SQL, reading/writing to files etc.). I don't want each DAO to throw exceptions specific to their storage mechansim (SQL-related exceptions etc.). I want them to throw a CouldNotStoreException since that's the level of abstraction that the client is working at. Throwing a SQL-related or a File-related exception would expose the internal workings, and the client isn't particular interested in that. They just want to know if the read/write operation worked.

You can create your custom exception using the originating exception as a cause . That way you don't lose the original info surrounding your problem.

In the above I probably wouldn't handle each exception in each step as you've done. If processing can't continue after an exception I would simply wrap the whole code block in an exception handling block. It improves readability and you don't have to catch an exception and then (later on) check the processing status to see if you can carry on as normal (if you don't do this you're going to generate many exceptions for one original issue and that's not helpful).

I would consider whether multiple catch {} blocks per exception add anything (are you doing something different for each one?). Note that Java 7 allows you to handle multiple exception classes in one catch{} (I realise you're on Java 6 but I note this for completeness).

Finally perhaps you want to think about checked vs unchecked exceptions .

Doesn't exist a generic rule,it depends on your needs. You can throw a personal exception, and you can manage each known exception.

But pay attention, it is important what you want.

try{
   exec1();
   exec2(); // if exec1 fails,  it is not executed
}catch(){}

try{
   exec1();
}catch(){}

try{
   exec2(); // if exec1 fails,  it is  executed
}catch(){}

The main point of the exception mechanism is to reduce and group together handling code. You are handling them in the style typical for a language without excptions, like C: every occurrence has a separate handling block.

In most cases the best option is to surround the entire method code with a catch-all:

try {
 .... method code ...
} 
catch (RuntimeException e) { throw e; } 
catch (Exception e) { throw new RuntimeException(e); }

The only times where this is not appropriate is where you want to insert specific handling code, or wrap in a custom exception that will be specifically handled later.

Most exceptions, especially IOExcption in your case, represent nothing else but failure and there will be no handling beyond logging it and returning the application to a safe point, where it can process further requests. If you find yourself repeating the same handling code over and over, it's a signal that you are doing it wrong.

One very important rule: either handle or rethrow; never do both. That is what you are doing in your example: both logging and rethrowing. Most likely the rethrown exception will be caught further up in the call stack and logged again. Reading through the resulting log files is a nightmare, but unfortunately quite a familiar one.

int step = 0;

try
{
    step = 1;
    ...
    step = 2;
    ...
    step = 3;
    ...
}
catch (Exception1 e)
{
    log ("Exception1 at step " + step);
    throw new MyException1 ("Step: " + step, e);
}
catch (Exception2 e)
{
    log ("Exception2 at step " + step);
    throw new MyException2 ("Step: " + step, e);
}
...

I'd say it depends on your needs...

If step2 can execute correctly even if step1 failed, you can try/catch step1 separately.
Otherwise, I would group all steps in one try/catch block and made sure that the individual steps produce a log message when they fail. That way you don't litter your code and still know what went wrong

It's ok to catch each known exception, so you can log what exception occure, and why it did.

Here some links to exception handling patterns/anti-patterns:

Do:

Don't:

About creating your own exceptions, it's certainly useful if you're creating an API, a framework or another piece of reusable code, but in a regular application, it's more debatable and I personally would suggest to stick to existing exceptions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM