1. Java常见异常类
下面显示了常见异常类之间的继承关系:
异常对象常用方法:
- getMessage():返回异常的详细描述字符串,例如:/ by zero
- printStackTrace():将异常的跟踪栈信息输出到标准错误输出
- printStackTrace(PrintStream s):将异常的跟踪栈信息输出到指定输出流
- getStackTrace():返回异常的跟踪栈信息
2. Runtime异常 和 Checked异常区别
Exception分为Runtime异常和Checked异常,RuntimeException类或其子类 被称为Runtime异常;其它为Checked异常。区别如下:
2.1 Runtime异常:
表示无法处理的异常,程序有必要中断;throw Runtime异常,可以不必一定写try…catch 或 throws
2.2 Checked异常:
表示可以处理的异常;throw Checked异常,要用throws 或 try … catch,throws即希望调用方来处理,如果一直未得到处理,会将异常交给JVM,JVM会打印跟踪栈信息,并终止程序
2.3 为什么Runtime异常不用throws 或 有时看到说 Runtime异常不建议 tru…catch???
之所以不用在函数上声明,是因为不需要让调用者处理,当该异常发生,希望程序停止,因为在运行时,出现了无法继续运算的情况,希望程序停止后由程序员对代码进行修正。但我们通常还是使用try…catch 并记录异常,而不是让程序直接崩掉
通俗讲: checked 异常 和 RuntimeException 有着不同的使用目的,检查性异常 用来指示 一种调用方能够直接处理的异常情况(例如: 用户输入错误,程序可以直接捕获并处理,提示用户输入错误), 而RuntimeException 是用来指 调用方 本身无法 处理或回复 的程序错误(例如,你封装个库给别人用,当别人调用你库中某个方法是,需要传入某些参数,如果用户传入的参数不合法,你自己没办法处理,那么此刻你抛出的就应该是运行时异常)
3. @Transactional注解事务回滚分析
@Transactional 默认只回滚Runtime异常,为什么只能回滚Runtime异常,个人任务:Runtime异常是无法预知的异常,无法预知是什么错该怎么处理,所以干脆就回滚所有;但Check异常,属于可被检查的异常,我这理解为可预知异常,都能预知,所以我们可以处理当遇到这种错误时该做什么错做,比如只撤销几步操作。
所以在使用@Transactional 最好控制下回滚的范围@Transactional(rollbackFor = Throwable.class
注意点:
- 只有代理类调用才会走进事务,即Bean调用,所以被@Transactional标注的方法一定是public,protect也不行,无法正常代理
- 内部调用无法正常代理,可以通过Bean调用
@Service
public class Test {
@Autowired
private Test test;
public void b() {
//这样事务没生效
this.a();
//这样事务生效了
test.a();
}
@Transactional(rollbackFor = Throwable.class)
public void a() {
}
}
4. 打印不完整的堆栈信息
最近出现 java.lang.ArithmeticException: null
异常,但是没有给出详细的堆栈信息,但是发现最初有java.lang.ArithmeticException: /by zero
异常,发现是由于1/0,除数为0情况造成的,但奇怪的是后来即使再有1/0情况也不报java.lang.ArithmeticException: /by zero
异常,反而只出现了 java.lang.ArithmeticException: null
这一行异常信息,奇怪的是没有完整堆栈信息,只有这个简要的错误信息,就猜测还是1/0造成的此错误,修改1/0错误后,确实就没再出现java.lang.ArithmeticException: null
异常。
经查资料得知:JVM在多次遇到同一异常信息时,前几次会输出堆栈信息,后面就会主动优化掉,只反馈异常摘要信息,即JIT重新编译后会抛出没有堆栈的异常,而在使用-server模式时,该优化选项是开启的,因此在频繁抛出某个异常一段时间后,该优化开始起作用,即只抛出没有堆栈的异常信息,这种异常抛出速度非常快,因为不需要在堆里分配内存,也不需要构造完整的异常栈信息.所以遇到这种情况,往前翻日志就可以看到异常的具体信息了。可以在jvm启动参数增加 -XX:-OmitStackTraceInFastThrow
,字面意思:忽略异常栈信息从而快速抛出,就可以始终抛出含异常的堆栈信息了,但是我们运行的项目不可能去重启去修改此参数,修改默认的jvm参数还是需要一定经验的,所以就去翻前的日志就可以了
还有一点疑惑:就算是优化也应该是只打印java.lang.ArithmeticException: /by zero
,而不是java.lang.ArithmeticException: null
???e.getMessage()message怎么也会变化那,难道是连错误信息也会优化???欢迎广大朋友留言
一般不会出现这种情况,如果想在本地复现,可以使用如下代码测试:
public void test() {
for (int i = 0; i < 200000; i++) {
try {
System.out.println(i);
int ii = 1 / 0;
} catch (Exception e) {
// 由 2 变为了 0
System.out.println(e.getStackTrace().length);
// 由 / by zero 变为了 null
System.out.println(e.getMessage());
}
}
}
会发现
- e.getStackTrace().length输出由2会变为0
- e.getMessage() 输出 由/ by zero 变为了null
但是如果设置 -XX:-OmitStackTraceInFastThrow
,
会发现
- e.getStackTrace().length永远输出2
- e.getMessage() 永远输出 / by zero