继承关系

概念

  1. Exception:程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相关处理
    • Checked Exception:源代码显式捕获处理,编译期检查,设计初衷为从异常情况中恢复
    • Unchecked Exception(RuntimeException):可以编码避免的逻辑错误,不会在编译期强制要求
  2. Error:在正常情况下,不太可能出现,绝大部分的Error都会导致程序处于不可恢复的状态

ClassNotFoundException

1
2
3
4
5
Thrown when an application tries to load in a class through its string name using:
The forName method in class Class.
The findSystemClass method in class ClassLoader.
The loadClass method in class ClassLoader.
but no definition for the class with the specified name could be found.

找不到.class文件

NoClassDefFoundError

1
2
3
Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class
(as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.
The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.

能找到.class文件,但ClassLoader尝试加载类的定义时却找不到该类的定义

最佳实践

反例1

1
2
3
4
5
6
try {
// 业务代码
Thread.sleep(1000L);
} catch (Exception e) {
// 忽略
}
  1. 不要捕获通用异常Exception,应该捕获特定异常InterruptedException
    • 不要捕获ThrowableError,否则很难保证能够正常处理OutOfMemoryError
  2. 不要生吞异常,出现故障后难以诊断

反例2

1
2
3
4
5
6
try {
// 业务代码
} catch (IOException e) {
// Prints this throwable and its backtrace to the standard error stream.
e.printStackTrace();
}
  1. 在复杂的生产环境中,stderr不是一个合适的输出选项,很难判断输出到哪里去了
  2. 最佳实践:使用产品日志,输出到日志系统

Throw early, catch late

Throw early

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws FileNotFoundException {
readFile(null);
}

private static void readFile(String fileName) throws FileNotFoundException {
InputStream in = new FileInputStream(fileName);
}

// 异常信息不直观
Exception in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:130)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at me.zhongmingmao.Main.readFile(Main.java:14)
at me.zhongmingmao.Main.main(Main.java:9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws FileNotFoundException {
readFile(null);
}

private static void readFile(String fileName) throws FileNotFoundException {
Objects.requireNonNull(fileName);
InputStream in = new FileInputStream(fileName);
}

// 使用Throw early,异常信息比较直观
Exception in thread "main" java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at me.zhongmingmao.Main.readFile(Main.java:14)
at me.zhongmingmao.Main.main(Main.java:10)

Catch late

捕获异常后,如果实在不知道如何处理,可以保留原有异常的cause信息,直接再抛出或者构建新的异常抛出

自定义异常

  1. 是否定义成Checked ExceptionChecked Exception的设计初衷是为了从异常情况中恢复
    • 我们作为异常的设计者,是有充足的信息对异常进行分类的,是否满足Checked Exception的设计初衷
  2. 在保证诊断信息足够的同时,也需要避免包含敏感信息(例如用户信息),可能会导致潜在的安全问题

性能开销

  1. try-catch会产生额外的性能开销,往往会影响JVM对代码进行优化
    • 尽量仅捕获有必要的代码块,尽量不要使用大try-catch
    • 不要使用try-catch控制代码流程,比常规的条件语句(if/else,switch)要低效
  2. 每实例化一个Exception,都需要对当时的进行快照,这是一个比较重的操作
    • 当服务吞吐量下降时,可以考虑检查发生最频繁的Exception