异常是怎么被处理的?这题的答案不在源码里面。 (下)

简介: 异常是怎么被处理的?这题的答案不在源码里面。 (下)

怎么样,发现了没?就问你神不神奇?

在源码中,只在 finally 代码块出现过一次的输出语句,在字节码中出现了三次。

finally 代码块中的代码被复制了两份,分别放到了 try 和 catch 语句的后面。再配合异常表使用,就能达到 finally 语句一定会被执行的效果。

以后再也不怕面试官问你为什么 finally 一定会执行了。

虽然应该也没有面试官会问这样无聊的问题。

问起来了,就从字节码的角度给他分析一波。

当然了,如果你非要给我抬个杠,聊聊 System.exit 的情况,就没多大意义了。

最后,关于 finally,再讨论一下这个场景:

public class MainTest {
    public static void main(String[] args) {
        try {
            int a = 1 / 0;
        } finally {
            System.out.println("final");
        }
    }
}

这个场景下,没啥说的, try 里面抛出异常,触发 finally 的输出语句,然后接着被抛出去,打印在控制台:

image.png

如果我在 finally 里面加一个 return 呢?

可以看到,运行结果里面异常都没有被抛出来:


image.png


为什么呢?

答案就藏在字节码里面:


image.png

其实已经一目了然了。

右边的 finally 里面有 return,并没有 athrow 指令,所以异常根本就没有抛出去。

这也是为什么建议大家不要在 finally 语句里面写 return 的原因之一。


冷知识


再给大家补充一个关于异常的冷知识吧。

image.png

还是上面这个截图。你有没有觉得有一丝丝的奇怪?

夜深人静的时候,你有没有想过这样的一个问题:

程序里面并没有打印日志的地方,那么控制台的日子是谁通过什么地方打印出来的呢?

是谁干的?

这个问题很好回答,猜也能猜到,是 JVM 帮我们干的。

什么地方?

这个问题的答案,藏在源码的这个地方,我给你打个断点跑一下,当然我建议你也打个断点跑一下:

java.lang.ThreadGroup#uncaughtException


image.png

image.png

而在这个地方打上断点,根据调用堆栈顺藤摸瓜可以找到这个地方:

java.lang.Thread#dispatchUncaughtException


image.png

看方法上的注释:

This method is intended to be called only by the JVM.

翻译过来就是:这个方法只能由 JVM 来调用。

既然源码里面都这样说了,我们可以去找找对应的源码嘛。

https://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/5b9a416a5632/src/share/vm/runtime/thread.cpp

在 openJdk 的 thread.cpp 源码里面确实是找到了该方法被调用的地方:


image.png

而且这个方法还有个有意思的用法。

看下面的程序和输出结果:

image.png

我们可以自定义当前线程的 UncaughtExceptionHandler,在里面做一些兜底的操作。

有没有品出来一丝丝全局异常处理机制的味道?

好了,再来最后一个问题:

image.png

我都这样问了,那么答案肯定是不一定的。

你就想想,发挥你的小脑袋使劲的想,啥情况下 try 里面的代码抛出了异常,外面的 catch 不会捕捉到?

来,看图:

image.png

没想到吧?

这样处理一下,外面的 catch 就捕捉不到异常了。

是不是很想打我。

别慌,上面这样套娃多没意思啊。

你再看看我这份代码:

public class MainTest {
    public static void main(String[] args) {
        try {
            ExecutorService threadPool = Executors.newFixedThreadPool(1);
            threadPool.submit(()->{
               int a=1/0;
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

你直接拿去执行,控制台不会有任何的输出。

来看动图:

image.png

是不是很神奇?

不要慌,还有更绝的。

把上面的代码从 threadPool.submit 修改为 threadPool.execute 就会有异常信息打印出来了:

image.png

但是你仔细看,你会发现,异常信息虽然打印出来了,但是也不是因为有 catch 代码块的存在。

具体是为啥呢?

参见这篇文章,我之前详细讲过的:《关于多线程中抛异常的这个面试题我再说最后一次!》


最后说一句


好了,看到了这里安排个点赞吧。感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

目录
相关文章
已知2019年是猪年,请在控制台输出从1949年到2019年中所有是猪年的年份
已知2019年是猪年,请在控制台输出从1949年到2019年中所有是猪年的年份
127 1
已知2019年是猪年,请在控制台输出从1949年到2019年中所有是猪年的年份
|
缓存 Linux Docker
44-Dockerfile-ADD/COPY指令
44-Dockerfile-ADD/COPY指令
|
程序员 开发者
05加班越久故障越多,如何跳出程序员的恶性循环?|学习笔记
快速学习05加班越久故障越多,如何跳出程序员的恶性循环?
130 0
|
人工智能
广州:港澳青年来穗创业 最高可获450万元补助
近日,广州发布《2021年港澳青年来穗创新创业补助申报指南》支持港澳青年来穗创新创业...
|
4天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
354 105
|
5天前
|
JSON fastjson Java
FastJson 完全学习指南(初学者从零入门)
摘要:本文是FastJson的入门学习指南,主要内容包括: JSON基础:介绍JSON格式特点、键值对规则、数组和对象格式,以及嵌套结构的访问方式。FastJson是阿里巴巴开源的高性能JSON解析库,具有速度快、功能全、使用简单等优势,并介绍如何引入依赖,如何替换Springboot默认的JackJson。 核心API: 序列化:将Java对象转换为JSON字符串,演示对象、List和Map的序列化方法; 反序列化:将JSON字符串转回Java对象,展示基本对象转换方法;