Java: gracefully handle exceptions is really a learning ah!

This article, we talk about how to gracefully handle an exception .

01, exception handling mechanism can be a little bug

Do you have the impression that when you want to update an APP, its update log there is always such a twelve description:

  • Several bug fix
  • Kill a programmer heaven, and successfully get rid of his legacy bug

As a responsible programmer, we certainly hope that the program will not bug, because the more bug appears, indirectly confirms our programming capabilities worse, at least the leaders think so.

In fact, leadership is not going to take his head declaration: "Our program does not even exist any bug." But when a program bug appears when the leadership will not hesitate to let programmers choose the back pan.

In order to make their own little pot back, we can do this:

  • Rational use of exception handling in the coding stage, and logs for subsequent analysis
  • A lot effective test in the testing phase, the user finds an error before the error found

Another point that needs to be done, before knocking the code, learn the necessary knowledge of programming, so that moving troops and horses, forage first.

02, abnormal classification

In Java, exception (the Throwable) hierarchy as follows.

Java: gracefully handle exceptions is really a learning ah!

Error Abnormal describes internal error class system running Java, such as the most common OutOfMemoryErrorand NoClassDefFoundError.

Cause OutOfMemoryErrorthere are several common reasons:

  • The amount of data loaded in memory is too large, too much data as extracted from a database;
  • Collection object reference is not emptied after use, it can not be recycled so that the JVM;
  • Infinite loop or cycle is repeated excessive presence object code;
  • Parameter values ​​set in boot memory is too small;

OutOfMemoryError The solution requires, as the case may be, but the root of the problem lies in the design process is not reasonable, we need some performance testing to find the root cause come to cause problems.

Leading to NoClassDefFoundErrorthe cause of only one, Java virtual machine can find the class at compile time, but could not find running Shique.

Java: gracefully handle exceptions is really a learning ah!

NoClassDefFoundErrorThe solution, I cut a picture, as shown above. When a project references another project, remember this step!

Exception(例外)通常可分为两类,一类是写代码的人造成的,比如访问空指针(NullPointerException)。应当在敲代码的时候进行检查,以杜绝这类异常的发生。

if (str == null || "".eqauls(str)) {
}

另外一类异常不是写代码的人造成的,要么需要抛出,要么需要捕获,比如说常见的 IOException

抛出的示例。

public static void main(String[] args) throws IOException {
    InputStream is = new FileInputStream("沉默王二.txt");
    int b;
    while ((b = is.read()) != -1) {

    }
}

捕获的示例。

public static void main(String[] args) {
    try {
        InputStream is = new FileInputStream("沉默王二.txt");
        int b;
        while((b = is.read()) != -1) {

        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

03、finally

当抛出异常的时候,剩余的代码就会终止执行,这时候一些资源就需要主动回收。Java 的解决方案就是 finally 子句——不管异常有没有被捕获,finally 子句里的代码都会执行。

在下面的示例当中,输入流将会被关闭,以释放资源。

public static void main(String[] args) {
    InputStream is = null;
    try {
        is = new FileInputStream("沉默王二.txt");
        int b;
        while ((b = is.read()) != -1) {}
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        is.close();
    }
}

但我总觉得这样的设计有点问题,因为 close() 方法同样会抛出 IOException

    public void close() throws IOException {}

也就是说,调用 close() 的 main 方法要么需要抛出 IOException,要么需要在 finally 子句里重新捕获 IOException

选择前一种就会让 try catch 略显尴尬,就像下面这样。

public static void main(String[] args) throws IOException {
    InputStream is = null;
    try {
        is = new FileInputStream("沉默王二.txt");
        int b;
        while ((b = is.read()) != -1) {}
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        is.close();
    }
}

选择后一种会让代码看起来很臃肿,就像下面这样。

public static void main(String[] args) {
    InputStream is = null;
    try {
        is = new FileInputStream("沉默王二.txt");
        int b;
        while ((b = is.read()) != -1) {}
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

总之,我们需要另外一种更优雅的解决方案。JDK7 新增了 Try-With-Resource 语法:如果一个类(比如 InputStream)实现了 AutoCloseable 接口,那么就可以将该类的对象创建在 try 关键字后面的括号中,当 try-catch 代码块执行完毕后,Java 会确保该对象的 close方法被调用。示例如下。

public static void main(String[] args) {
    try (InputStream is = new FileInputStream("沉默王二.txt")) {
        int b;
        while ((b = is.read()) != -1) {
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

04、实用建议

关于异常处理机制的使用,我这里总结了一些非常实用的建议,希望你能够采纳。

1)尽量捕获原始的异常

实际应该捕获 FileNotFoundException,却捕获了泛化的 Exception。示例如下。

InputStream is = null;
try {
    is = new FileInputStream("沉默王二.txt");
} catch (Exception e) {
    e.printStackTrace();
}

这样做的坏处显而易见:假如你喊“王二”,那么我就敢答应;假如你喊“老王”,那么我还真不敢答应,万一你喊的我妹妹“王三”呢?

很多初学者误以为捕获泛化的 Exception 更省事,但也更容易让人“丈二和尚摸不着头脑”。相反,捕获原始的异常能够让协作者更轻松地辨识异常类型,更容易找出问题的根源。

2)尽量不要打印堆栈后再抛出异常

当异常发生时打印它,然后重新抛出它,以便调用者能够适当地处理它。就像下面这段代码一样。

public static void main(String[] args) throws IOException {
    try (InputStream is = new FileInputStream("沉默王二.txt")) {
    }catch (IOException e) {
        e.printStackTrace();
        throw e;
    } 
}

这似乎考虑得很周全,但是这样做的坏处是调用者可能也打印了异常,重复的打印信息会增添排查问题的难度。

java.io.FileNotFoundException: 沉默王二.txt (系统找不到指定的文件。)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at learning.Test.main(Test.java:10)
Exception in thread "main" java.io.FileNotFoundException: 沉默王二.txt (系统找不到指定的文件。)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at learning.Test.main(Test.java:10)

3)千万不要用异常处理机制代替判断

我曾见过类似下面这样奇葩的代码,本来应该判 null 的,结果使用了异常处理机制来代替。

public static void main(String[] args) {
    try {
        String str = null;
        String[] strs = str.split(",");
    } catch (NullPointerException e) {
        e.printStackTrace();
    }
}

捕获异常相对判断花费的时间要多得多!我们可以模拟两个代码片段来对比一下。

代码片段 A:

long a = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    try {
        String str = null;
        String[] strs = str.split(",");
    } catch (NullPointerException e) {
    }
}
long b = System.currentTimeMillis();
System.out.println(b - a);

代码片段 B:

long a = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    String str = null;
    if (str != null) {
        String[] strs = str.split(",");
    }
}
long b = System.currentTimeMillis();
System.out.println(b - a);

100000 万次的循环,代码片段 A(异常处理机制)执行的时间大概需要 1983 毫秒;代码片段 B(正常判断)执行的时间大概只需要 1 毫秒。这样的比较虽然不够精确,但足以说明问题。

4)不要盲目地过早捕获异常

如果盲目地过早捕获异常的话,通常会导致更严重的错误和其他异常。请看下面的例子。

InputStream is = null;
try {
    is = new FileInputStream("沉默王二.txt");

} catch (FileNotFoundException e) {
    e.printStackTrace();
}

int b;
try {
    while ((b = is.read()) != -1) {
    }
} catch (IOException e) {
    e.printStackTrace();
}

finally {
    try {
        is.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

假如文件没有找到的话,InputStream 的对象引用 is 就为 null,新的 NullPointerException 就会出现。

java.io.FileNotFoundException: 沉默王二.txt (系统找不到指定的文件。)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at learning.Test.main(Test.java:12)
Exception in thread "main" java.lang.NullPointerException
    at learning.Test.main(Test.java:28)

NullPointerException 并不是程序出现问题的本因,但实际上它出现了,无形当中干扰了我们的视线。正确的做法是延迟捕获异常,让程序在第一个异常捕获后就终止执行。

05、最后

好了,关于异常我们就说到这。异常处理是程序开发中必不可少的操作之一,但如何正确优雅地对异常进行处理却是一门学问,好的异常处理机制可以确保程序的健壮性,提高系统的可用率。

Previous: the Java interviewer: Brother, are you sure double precision is lower than float it?

Next: talk about Java's inheritance and superclass Object

Guess you like

Origin blog.51cto.com/2324584/2450294