Java Exception

参考文章:
Checked vs Unchecked Exception
Checked exceptions: Java’s biggest mistake
try-catch-finally


1. Throwable的继承体系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Object
└── Throwable
├── Error(unchecked)
│   ├── LinkageError
│   ├── NoClassDefFoundError
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── VirtualMachineError...
└── Exception
├── RuntimeException(unchecked)
│   ├── ArrayIndexOutOfBoundsException
│   ├── ClassCastException
│   ├── NullPointerException
│   └── SecurityException...
└── others(checked)
├── ClassNotFoundException
└── IOException...

受检查异常(checked) : 除了RuntimeException及其子类之外的异常, JVM强制方法的调用者进行处理, 主要针对可恢复的异常。
非受检查(unchecked) : RuntimeException和Error。


2. Exception vs Error

例如: NoClassDefFoundError与ClassNotFoundException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.quantums;

import java.util.Scanner;


public class TestExceptionError {

public static void main(String[] ar) {
//1. ClassNotFoundException
classNotFoundException();

//2. NoClassDefFoundError
deleteByteCode();

//3.
loadClassFailed();
}

private static void classNotFoundException() {
try {
Scanner scanner = new Scanner(System.in);

/**利用scanner阻塞代码, 先手动删除class的字节码, 或者设置错误的路径**/
// int next = scanner.nextInt();
Class cl = Class.forName("com.quantums.wrong.NormalClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

private static void deleteByteCode() {
Scanner scanner = new Scanner(System.in);

/**利用scanner阻塞等待, 这个过程中手动去删掉NormalClass的字节码**/
int next = scanner.nextInt();
NormalClass n = new NormalClass();
}


private static void loadClassFailed() {
FailedInitializationClass cl = new FailedInitializationClass();
}
}

class NormalClass {

}

/**
* 这是一个加载时会失败的class
*/
class FailedInitializationClass {
static {
System.out.println("Class Loading Failed:");
int a = 1 / 0;
}

public FailedInitializationClass() {

}
}

3. Checked Exception vs Unchecked Exception

  • 1. Unchecked Exception:

    Unchecked exceptions are those that extend RuntimeException class. Compiler will never force you to catch such exception or force you to declare it in the method using throws keyword. All other exception types (that do not extend RuntimeException) are checked and therefore must be declared to be thrown and/or catched.

  • 2. Checked Exception:

    Checked exceptions are used when you want the caller of your method (i.e the user of your API) to explicitly handle the exceptional case in your API. Checked exceptions are declared when you believe the call will be able to do something meaningful with that exceptional case, like retrying the call, rolling changes back or converting it into some user-readable error message.

  • 3. So:

    If you believe that there is nothing useful the call can do about the exception (especially when it represents a bug, or a wrong usage of your API), then the exception should be unchecked. Also, an API with too many checked exceptions can be annoying to program with (e.g. try using java reflection API=)


4. try-catch-finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.quantums.chap7;

public class TestTryCatchFinally {

public static void main(String[] ar) {
System.out.println("方法最终返回结果="+test());
}


/**
* 1、不管有没有异常,finally中的代码都会执行
*
* 2、当try、catch中有return时,finally中的代码依然会继续执行  
*
* 3、finally是在return后面的表达式运算之后执行的,此时并没有返回运算之后的值,而是把值保存起来,不管finally对该值做任何的改变,返回的值都不会改变,依然返回保存起来的值。也就是说方法的返回值是在finally运算之前就确定了的。
*
* 4、finally代码中最好不要包含return,程序会提前退出,也就是说返回的值不是try或catch中的值
*/
private static String test() {
String a = null;
try {
if (a.equals("Hello")) ;
return a;
} catch (Exception e) {
a = "catch";
System.out.println("catch执行之后a="+a);
return a;
} finally {
a = "finally";
System.out.println("finally执行之后: a="+a);
// finally代码中最好不要包含return,程序会提前退出,也就是说返回的值不是try或catch中的值
return a;
}
}
}

5. Java异常性能分析

从性能角度分析Java异常机制, 这里有两个相对低效的地方:

5.1 try-catch代码段额外的性能开销

try-catch代码段会阻止JVM对这段代码进行优化, 所以尽量别用一个try包括大段代码,仅包含必要部分即可。

5.2 实例化Exception开销大

Java每次实例化一个Exception, 都会对当前的栈进行快照,这是一个相对比较重的操作


6. 使用规范

    1. 尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常
    1. 不要生吞(swallow)异常(catch了却不做任何动作)
    1. 不用e.printStackTrace():”Prints this throwable and its backtrace to standard err stream”, STDERR不是输出异常的好地方
    1. 不要使用try-catch来控制代码流程, 它的性能比if-else之类的低