Java异常(Runtime异常 和 Checked异常)

1. Java常见异常类

下面显示了常见异常类之间的继承关系:
Java异常(Runtime异常 和 Checked异常)_第1张图片
异常对象常用方法:

  • 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

注意点:

  1. 只有代理类调用才会走进事务,即Bean调用,所以被@Transactional标注的方法一定是public,protect也不行,无法正常代理
  2. 内部调用无法正常代理,可以通过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参数还是需要一定经验的,所以就去翻前的日志就可以了(推荐文章:https://www.jianshu.com/p/cc1bd35466cb)。
    还有一点疑惑:就算是优化也应该是只打印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());
            }
        }
    }

会发现

  1. e.getStackTrace().length输出由2会变为0
  2. e.getMessage() 输出 由/ by zero 变为了null

但是如果设置 -XX:-OmitStackTraceInFastThrow
在这里插入图片描述
会发现

  1. e.getStackTrace().length永远输出2
  2. e.getMessage() 永远输出 / by zero

你可能感兴趣的:(JAVA)