简体   繁体   中英

Why should I use Exception handlings in Java?

I read many posts on SO about java exception handling but I did get satisfy answer yet.Why should I put them in my codes ?

  1. I would like to use some api methods of JRE and these are made with checked exceptions. So if I want to use them , I need to throw or catch exceptions (eg.Java I/O). Is this the reasonable rules to use exceptions in my class ?
  2. I heard about this

Java exception handlings make separating error handling codes from my business logic

Where is separating error handling in below snippet ?

public int division(int divident, int divisor) {
int result = 0;
try {
    result = divident / divisor;
} catch (ArithmeticException e) {
    System.out.println("Divisor must not be zero !");
}
return result;
}

3. Java's default exception handling makes showing exception information in standard output and terminate the program. Am I use exception handling by myself to avoid terminating from my programs?

It's three questions, but it's not like that's the first time that's happened here. :-)

  1. Yes, you must handle them or declare them, because they're checked exceptions. The reason you must do that is so that the code calling your code knows the ways in which your code may fail (because you've declared the exceptions it can throw).

  2. That snippet is very simple, so the separation and benefit aren't all that clear. But consider opening two streams, copying from one to another, with transformation code (including calls to subordinate methods). You end up with a method body with 10-20 statements in it. Instead of every I/O statement having to check whether it worked or not, you just write your logic wrapped in an IOException handler, knowing that any I/O exceptions will jump out of the main logic into the handler.

  3. It depends on what kind of program you're writing, but in general you handle exceptions at the most appropriate level, which is typically at multiple levels in your program. The outermost level, which is only to deal with really, really unusual, unrecoverable exceptions, would either just let the default handling do what it does, or use a catch-all handler that does something similar but might (attempt to) record the failure elsewhere (like a log file) as well:

     public class MyProgram { public static final void main(String[] args) { try { // Run... } catch (Throwable t) { // Handle the fact that something went wrong here, if you can // Usually this would be only for really, really unusual errors, // otherwise you would have handled them earlier } } } 

To underscore the point in #2, consider two process methods, one in Java with exceptions, and the other in a hypothetical Java-like language that doesn't have exceptions:

The Java one:

private void process() {
    try (                                                // <== Main logic
        Reader fr = new FileReader(this.sourceFileName); // <== Main logic
        BufferedReader br = new BufferedReader(fr);      // <== Main logic
        Writer fw = new FileWriter(this.destFileName);   // <== Main logic
        BufferedWriter bw = new BufferedWriter(fw)       // <== Main logic
        ) {                                              // <== Main logic
        String line;                                     // <== Main logic
        while ((line = br.readLine()) != null) {         // <== Main logic
            if (shouldIncludeLine(line)) {               // <== Main logic
                line = transformLine(line);              // <== Main logic
                bw.write(line);                          // <== Main logic
                bw.newLine();                            // <== Main logic
            }                                            // <== Main logic
        }                                                // <== Main logic
    }
    catch (FileNotFoundException fnfe) {                 // <== Error handling
        // Couldn't find a file                          // <== Error handling
        // (handle it)                                   // <== Error handling
    }                                                    // <== Error handling
    catch (IOException ioe) {                            // <== Error handling
        // I/O error                                     // <== Error handling
        // (handle it)                                   // <== Error handling
    }                                                    // <== Error handling
    catch (Exception e) {                                // <== Error handling
        // Something else went wrong                     // <== Error handling
        // (handle it)                                   // <== Error handling
    }                                                    // <== Error handling
}

The hypothetical Java-like language without exceptions one:

// THIS IS FAKE, PSEUDO-JAVA
private Errors process() {
    Reader fr = new FileReader(this.sourceFileName);            // <== Main logic
    if (fr == null) {                                           // <== Error handling
        return Errors.CantOpenSource;                           // <== Error handling
    }                                                           // <== Error handling
    BufferedReader br = new BufferedReader(fr);                 // <== Main logic

    Writer fw = new FileWriter(this.destFileName);              // <== Main logic
    if (fw == null) {                                           // <== Error handling
        br.close();                                             // <== Error handling
        return Errors.CantOpenDest;                             // <== Error handling
    }                                                           // <== Error handling
    BufferedWriter bw = new BufferedWriter(fw)                  // <== Main logic

    String line;                                                // <== Main logic
    while ((line = br.readLine()) != IO.END_OF_FILE) {          // <== Main logic
        if (line == null) {                                     // <== Error handling
            br.close();                                         // <== Error handling
            bw.close();                                         // <== Error handling
            return Errors.CantRead;                             // <== Error handling
        }
        if (shouldIncludeLine(line)) {                          // <== Main logic
            line = transformLine(line);                         // <== Main logic
            if (bw.write(line) == -1 || bw.newLine() == -1) {   // <== Main logic (plus some error handling)
                br.close();                                     // <== Error handling
                bw.close();                                     // <== Error handling
                return Errors.CantWrite;                        // <== Error handling
            }
        }
    }

    bw.close();
    br.close();
    return Errors.Success;
}

Notice:

  • How the main logic is littered with error handling, making it harder to read and follow.
  • Special "error" return values are necessary for any method that might possibly have some kind of failure mode. We had to add one to process , and then we check for null from new FileReader and such, and check for -1 from read and write ops, etc.

If you're interested, here's a full version of the Java program vs. the full version of the not-really-Java program:

Java:

import java.io.*;

public class Example
{
    private String sourceFileName;
    private String destFileName;

    public static void main (String[] args) throws java.lang.Exception
    {
        try {
            new Example(args[0], args[1]).process();
        }
        catch (ArrayIndexOutOfBoundsException npe) {
            // This is a bit of an exaggeration, I'd check in advance, since the user not
            // supplying arguments isn't really an "exceptional" condition.
            System.out.println("Usage: java Example [source file name] [dest file name]");
        }
    }

    public Example(String src, String dest) {
        // Similar, these checks would probably be assertions, but I'm making a point...
        if (src == null || src.length() == 0) {
            throw new IllegalArgumentException("src must be non-null and non-blank");
        }
        if (dest == null || dest.length() == 0) {
            throw new IllegalArgumentException("dest must be non-null and non-blank");
        }
        this.sourceFileName = src;
        this.destFileName = dest;
    }

    private void process() {
        try (                                                // <== Main logic
            Reader fr = new FileReader(this.sourceFileName); // <== Main logic
            BufferedReader br = new BufferedReader(fr);      // <== Main logic
            Writer fw = new FileWriter(this.destFileName);   // <== Main logic
            BufferedWriter bw = new BufferedWriter(fw)       // <== Main logic
            ) {                                              // <== Main logic
            String line;                                     // <== Main logic
            while ((line = br.readLine()) != null) {         // <== Main logic
                if (shouldIncludeLine(line)) {               // <== Main logic
                    line = transformLine(line);              // <== Main logic
                    bw.write(line);                          // <== Main logic
                    bw.newLine();                            // <== Main logic
                }                                            // <== Main logic
            }                                                // <== Main logic
        }
        catch (FileNotFoundException fnfe) {                 // <== Error handling
            // Couldn't find a file                          // <== Error handling
            // (handle it)                                   // <== Error handling
        }                                                    // <== Error handling
        catch (IOException ioe) {                            // <== Error handling
            // I/O error                                     // <== Error handling
            // (handle it)                                   // <== Error handling
        }                                                    // <== Error handling
        catch (Exception e) {                                // <== Error handling
            // Something else went wrong                     // <== Error handling
            // (handle it)                                   // <== Error handling
        }                                                    // <== Error handling
    }

    private boolean shouldIncludeLine(String line) {
        return line.length() != 0;
    }

    private String transformLine(String line) {
        return line.toUpperCase();
    }
}

The hypothetical Java-like language without exceptions one:

// THIS IS FAKE, PSEUDO-JAVA WITHOUT EXCEPTIONS, IT ISN'T REAL
import java.io.*;

public class Example
{
    private String sourceFileName;
    private String destFileName;

    private enum Errors {
        Success,
        CantOpenSource,
        CantOpenDest,
        CantRead,
        CantWrite
    }

    public static void main (String[] args) throws java.lang.Exception
    {
        if (args.length < 2) {
            System.out.println("Usage: java Example [source file name] [dest file name]");
        }
        if (args[0] == null || args[0].length() == 0) {
            throw new IllegalArgumentException("src must be non-null and non-blank");
        }
        if (args[1] == null || args[1].length() == 0) {
            throw new IllegalArgumentException("dest must be non-null and non-blank");
        }
        switch (new Example(args[0], args[1]).process()) {
            case Errors.CantOpenSource:
                // Handle it
                break;
            case Errors.CantOpenDest:
                // Handle it
                break;
            case Errors.CantRead:
                // Handle it
                break;
            case Errors.CantWrite:
                // Handle it
                break;
        }
    }

    public Example(String src, String dest) {
        // Not how now this constructor is trusting that it is called with valid arguments
        this.sourceFileName = src;
        this.destFileName = dest;
    }

    private Errors process() {
        Reader fr = new FileReader(this.sourceFileName);            // <== Main logic
        if (fr == null) {                                           // <== Error handling
            return Errors.CantOpenSource;                           // <== Error handling
        }                                                           // <== Error handling
        BufferedReader br = new BufferedReader(fr);                 // <== Main logic

        Writer fw = new FileWriter(this.destFileName);              // <== Main logic
        if (fw == null) {                                           // <== Error handling
            br.close();                                             // <== Error handling
            return Errors.CantOpenDest;                             // <== Error handling
        }                                                           // <== Error handling
        BufferedWriter bw = new BufferedWriter(fw)                  // <== Main logic

        String line;                                                // <== Main logic
        while ((line = br.readLine()) != IO.END_OF_FILE) {          // <== Main logic
            if (line == null) {                                     // <== Error handling
                br.close();                                         // <== Error handling
                bw.close();                                         // <== Error handling
                return Errors.CantRead;                             // <== Error handling
            }
            if (shouldIncludeLine(line)) {                          // <== Main logic
                line = transformLine(line);                         // <== Main logic
                if (bw.write(line) == -1 || bw.newLine() == -1) {   // <== Main logic (plus some error handling)
                    br.close();                                     // <== Error handling
                    bw.close();                                     // <== Error handling
                    return Errors.CantWrite;                        // <== Error handling
                }
            }
        }

        bw.close();
        br.close();
        return Errors.Success;
    }

    private boolean shouldIncludeLine(String line) {
        return line.length() != 0;
    }

    private String transformLine(String line) {
        return line.toUpperCase();
    }
}

1) If it's unreasonable for your code to be able to handle an exception, you can catch the checked exceptions thrown from your API calls and wrap them in an unchecked exception. Make sure you save the original checked exception as a cause in the new unchecked exception.

2) Your example snippet is not separating error-handling from business logic, it's glomming them together and obfuscating the result. There are two benefits of throwing the arithmetic exception here rather than passing back a default value: a) it's too difficult to distinguish a value passed to flag an error from a value that is the result of a valid computation and b) further business logic steps may depend on having calculated a valid value here, in which case you will have to leave the current flow anyway, you might as well use the exception for that.

3) It depends on the application. For a simple console program sometimes the best thing to do is let the error terminate the program. For a web application exceptions usually bubble up to a global exception handler, terminating that request but letting other requests proceed. For unit tests an exception is caught by the test runner and logged so that other tests can proceed.

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