NoClassDefFoundError/ClassNotFoundException
NoClassDefFoundError发生在jvm运行时,执行某个方法或者静态成员时,如果jvm加载不到该类时报错的错误。本地编译没报错,但运行时报错,有可能该类对类加载器而言是不可见的。
ClassNotFoundException发生与编译时根据classpath找不到对应类时报的错误。
由于User类为"private"修饰,Test.java里的main无权访问,编译时不会报错到运行时因找到User类NotClassDefFoundError:
import java.util.ArrayList;
import java.util.List;
/**
* Created by fujianbo on 2018/5/12.
*
* @author fujianbo
* @date 2018/05/12
*/
public class Test {
public static void main(String args[]){
List<User> users = new ArrayList<>(2);
try {
users.add(new User("1234"));
} catch (Throwable t) {
t.printStackTrace();
}
}
}
class User{
private static String USER_ID = getUserId();
public User(String id){
this.USER_ID = id;
}
private static String getUserId() {
throw new RuntimeException("UserId Not found");
}
}
不良案例
捕获时异常要明确异常类型,提高代码可读性
public class Test {
public static void main(String args[]){
try {
Thread thread = new Thread();
// do something
// ...
thread.wait();
} catch (InterruptedException e) {
}
}
}
捕获异常范围不宜过大
RuntimeException更应该扩散而不是捕获(很多时候我们并不能处理这类异常),如Throwable或则Error也是如此。
不要使用标准输出打印异常
try {
Thread thread = new Thread();
// do something
// ...
thread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
使用e.printStackTrace将导致异常堆栈输出到标准异常流,这很难判断日志最终被输出都哪里而丧失监控的意义。
throw early & catch late原则
public static void main(String args[]){
loadFile(null);
}
private static void loadFile(String fileName) {
try {
FileInputStream fileInputStream = new FileInputStream(fileName);
func1(fileInputStream);
func2(fileInputStream);
} catch (FileNotFoundException e) {
// take logs here
}
}
private static void func1(FileInputStream fileInputStream) {
if (fileInputStream == null) {
return;
}
// do something
}
private static void func2(FileInputStream fileInputStream) {
// do something
}
throw early可节省查看堆栈排查问题的时间,无论使用断言还是if判断都可以简单做到。
自定义异常
- Checked Exception的初衷为从异常中恢复,哪些要恢复以及怎么恢复依赖于精细化的异常分类
- 异常信息不要包含敏感信息,这是ConnectionException不打印ip和端口信息的原因
Checked Exception Or Unchecked Exception?
- [引用自某博客,出处后续补上]虽然反对使用Checked Exception的声音不少,但Checked Exception在提示不要忘记处理异常,虽然调用该方法的方法体需要申明外抛异常,但至少可以指导底层会抛哪些异常,在自定义异常时提供了有效信息(异常种类划分&处理)
- Unchecked Exception使代码更简洁,但容易丢失详细堆栈信息。如调用链路横跨多个流程,每个流程涉及到的方法调用都有上下文,哪一步出错可以依赖不同流程的上下文打印信息,快速定位问题。
- try/catch代码块产生额外的性能开销,影响jvm优化(尽量缩小try/catch块的大小)
- 实例化一个Exception会保留执行时的堆栈快照,该过程为相对较重的操作。
思考
Reactive Stream(异步、基于事件机制),出现异常不能简单外抛。由于代码堆栈不在是同步调用时的垂直结构,我们看到的是特定executor的堆栈。那如何处理这类异常呢?(未完待续...)