# exception
TIP
java中的异常的超类是java.lang.Throwable(后文省略为Throwable),它有两个比较重要的子类,java.lang.Exception(后文省略为Exception)和java.lang.Error(后文省略为Error),其中Error由JVM虚拟机进行管理,如我们所熟知的OutOfMemoryError异常。
Exception异常有个比较重要的子类,叫做RuntimeException。我们将RuntimeException或其他继承自RuntimeException的子类称为非受检异常(unchecked Exception),其他继承自Exception异常的子类称为受检异常(checked Exception)。
Call Service Exception! com.netflix.zuul.exception.ZuulException: Forwarding error
超时时间不够。
# Java 异常处理的 10 个良心建议
- 1、尽量
不要使用e.printStackTrace(),而是使用log打印。- printStackTrace()打印出的堆栈日志跟业务代码日志是交错混合在一起的,排查异常日志不太方便。
- e.printStackTrace()语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,那么,用户的请求就卡住啦~
- 2、catch了异常,但是没有打印出具体的exception,无法更好定位问题
- 3、不要用一个Exception捕捉所有可能的异常
- 用基类 Exception 捕捉的所有可能的异常,如果多个层次都这样捕捉,会丢失原始异常的有效信息哦
- 4、记得使用finally关闭流资源或者直接使用try-with-resource
- 5、捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类
// 反例
//BizException 是 Exception 的子类
public class BizException extends Exception {}
//抛出父类Exception
public static void test() throws Exception {}
try {
test(); //编译错误
} catch (BizException e) { //捕获异常子类是没法匹配的哦
log.error(e);
}
// 正例
//抛出子类Exception
public static void test() throws BizException {}
try {
test();
} catch (Exception e) {
log.error(e);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 6、注意异常对你的代码层次结构的侵染(早发现早处理)
- 我们的项目,一般都会把代码分 Action、Service、Dao 等不同的层次结构,如果你是DAO层处理的异常,尽早处理吧,如果往上 throw SQLException,上层代码就还是要try catch处理啦,这就污染了你的代码~
// 反例
public UserInfo queryUserInfoByUserId(Long userid) throw SQLException {
//根据用户Id查询数据库
}
// 正例
public UserInfo queryUserInfoByUserId(Long userid) {
try{
//根据用户Id查询数据库
}catch(SQLException e){
log.error("查询数据库异常啦,{}",e);
}finally{
//关闭连接,清理资源
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 7、自定义封装异常,
不要丢弃原始异常的信息Throwable cause
我们常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。公司的框架提供统一异常处理就用到异常链,我们自定义封装异常,不要丢弃原始异常的信息,否则排查问题就头疼啦
// 反例
//MyException 构造器
public MyException(String message) {
super(message);
}
// 正例
//MyException 构造器
public MyException(String message, Throwable cause) {
super(message, cause);
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 8、运行时异常RuntimeException ,不应该通过catch 的方式来处理,而是先预检查,比如:NullPointerException处理
// 反例
try {
obj.method();
} catch (NullPointerException e) {
...
}
// 正例
if (obj != null){
...
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 9、注意异常匹配的顺序,优先捕获具体的异常
- 注意异常的匹配顺序,因为只有第一个匹配到异常的catch块才会被执行。
// 反例
try {
doSomething("test exception");
} catch (IllegalArgumentException e) {
log.error(e);
} catch (NumberFormatException e) {
log.error(e);
}
// 正例
try {
doSomething("test exception");
} catch (NumberFormatException e) {
log.error(e);
} catch (IllegalArgumentException e) {
log.error(e);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
因为NumberFormatException是IllegalArgumentException 的子类,反例中,不管是哪个异常,都会匹配到IllegalArgumentException,就不会再往下执行啦,因此不知道是否是NumberFormatException。所以需要优先捕获具体的异常,把NumberFormatException放前面~
# 内存泄露
- 静态集合类引起内存泄露
- 监听器:
- 但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
- 各种连接,数据库、网络、IO等
- 内部类和外部模块等的引用:
- 内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。
- 非静态内部类的对象会隐式强引用其外围对象,所以在内部类未释放时,外围对象也不会被释放,从而造成内存泄漏
- 单例模式:
- 不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象-的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露
- 其它第三方类