一、背景
对于很多初学者而言 会想当然地认为 “finally 代码块一定会被执行” 因此我们可以看下面这个案例
public class Demo {
? ?public static void main(String[] args) {
? ? ? ?try {
? ? ? ? ? ?BufferedReader br new BufferedReader(new FileReader( file.txt
? ? ? ? ? ?System.out.println(br.readLine());
? ? ? ? ? ?br.close();
? ? ? ?} catch (IOException e) {
? ? ? ? ? ?// 省略一些代码
? ? ? ?} finally {
? ? ? ? ? ?System.out.println( Exiting the program
? ? ? ?}
? ?}
}
问题是 该段代码 finally 的代码块一定会被执行吗 为什么
二、分析
通常实际编码时 捕获异常后会记录日志或者将异常抛出等 此时 finally 代码块一般肯定会被执行到。
那么如何才能不执行finally呢
于是我们想到 如果让虚拟机退出 问题不就解决了吗 (就是这么暴力)
因此填充代码
public class Demo {
? ?public static void main(String[] args) {
? ? ? ?try {
? ? ? ? ? ?BufferedReader br new BufferedReader(new FileReader( file.txt
? ? ? ? ? ?System.out.println(br.readLine());
? ? ? ? ? ?br.close();
? ? ? ?} catch (IOException e) {
? ? ? ? ? System.exit(2);
? ? ? ?} finally {
? ? ? ? ? ?System.out.println( Exiting the program
? ? ? ?}
? ?}
}
如果捕获到 IO异常 则会执行 ?虚拟机退出指令 则不会执行finally 代码块。
System#exit 的源码如下
? ?/**
? ? * Terminates the currently running Java Virtual Machine. The
? ? * argument serves as a status code; by convention, a nonzero status
? ? * code indicates abnormal termination.
? ? * p
? ? * This method calls the code exit /code method in class
? ? * code Runtime /code . This method never returns normally.
? ? * p
? ? * The call code System.exit(n) /code is effectively equivalent to
? ? * the call:
? ? * blockquote pre
? ? * Runtime.getRuntime().exit(n)
? ? * /pre /blockquote
? ? *
? ? * param ? ? ?status ? exit status.
? ? * throws ?SecurityException
? ? * ? ? ? ?if a security manager exists and its code checkExit /code
? ? * ? ? ? ?method doesn t allow exit with the specified status.
? ? * see ? ? ? ?java.lang.Runtime#exit(int)
? ? */
? ?public static void exit(int status) {
? ? ? ?Runtime.getRuntime().exit(status);
? ?}
通过注释我们可以了解到 当 status 为 非0 时 表示异常退出。
底层调用到 Runtime#exit
? ?/**
? ? * Terminates the currently running Java virtual machine by initiating its
? ? * shutdown sequence. ?This method never returns normally. ?The argument
? ? * serves as a status code; by convention, a nonzero status code indicates
? ? * abnormal termination.
? ? *
? ? * p The virtual machine s shutdown sequence consists of two phases. ?In
? ? * the first phase all registered { link #addShutdownHook shutdown hooks},
? ? * if any, are started in some unspecified order and allowed to run
? ? * concurrently until they finish. ?In the second phase all uninvoked
? ? * finalizers are run if { link #runFinalizersOnExit finalization-on-exit}
? ? * has been enabled. ?Once this is done the virtual machine { link #halt
? ? * halts}.
? ? *
? ? * p If this method is invoked after the virtual machine has begun its
? ? * shutdown sequence then if shutdown hooks are being run this method will
? ? * block indefinitely. ?If shutdown hooks have already been run and on-exit
? ? * finalization has been enabled then this method halts the virtual machine
? ? * with the given status code if the status is nonzero; otherwise, it
? ? * blocks indefinitely.
? ? *
? ? * p The tt { link System#exit(int) System.exit} /tt method is the
? ? * conventional and convenient means of invoking this method. p
? ? *
? ? * param ?status
? ? * ? ? ? ? Termination status. ?By convention, a nonzero status code
? ? * ? ? ? ? indicates abnormal termination.
? ? *
? ? * throws SecurityException
? ? * ? ? ? ? If a security manager is present and its tt { link
? ? * ? ? ? ? SecurityManager#checkExit checkExit} /tt method does not permit
? ? * ? ? ? ? exiting with the specified status
? ? *
? ? * see java.lang.SecurityException
? ? * see java.lang.SecurityManager#checkExit(int)
? ? * see #addShutdownHook
? ? * see #removeShutdownHook
? ? * see #runFinalizersOnExit
? ? * see #halt(int)
? ? */
? ?public void exit(int status) {
? ? ? ?SecurityManager security System.getSecurityManager();
? ? ? ?if (security ! null) {
? ? ? ? ? ?security.checkExit(status);
? ? ? ?}
? ? ? ?Shutdown.exit(status);
? ?}
三、延伸
同样的问题 请看下面代码片段
public class Demo {
? ?public static void main(String[] args) {
? ? ?// 一些代码
? ? ? ?try {
? ? ? ? ? ?BufferedReader br new BufferedReader(new FileReader( file.txt
? ? ? ? ? ?System.out.println(br.readLine());
? ? ? ? ? ?br.close();
? ? ? ?} catch (IOException e) {
? ? ? ? ? System.exit(2);
? ? ? ?} finally {
? ? ? ? ? ?System.out.println( Exiting the program
? ? ? ?}
? ?}
}
问题是 如果try 代码块部分发生IO异常 是否一定不会执行到 finally 代码块呢
what? ?上面不是说不会执行吗
我们再仔细看上面给出的 ?Runtime#exit 源码 可以发现 如果 SecurityManager 不为 null 则 会进行安全检查。
public void exit(int status) {
// 如果有securityManager , 则调用 checkExit函数
? ? ? ?SecurityManager security System.getSecurityManager();
? ? ? ?if (security ! null) {
? ? ? ? ? ?security.checkExit(status);
? ? ? ?}
// 检查通过后退出
? ? ? ?Shutdown.exit(status);
? ?}
安全检查通过才会执行 ? Shutdown#exit 执行最终的虚拟机退出。
因此如果我们可以修改 SecurityManager 如果检查退出时抛出异常 那么在 执行 ?System.exit(2) 时就会发生异常 最终依然会执行到 finally代码块。
public class Demo {
? ?public static void main(String[] args) {
? ? ? ?// 修改 SecurityManager
? ? ? ?System.setSecurityManager(new SecurityManager() {
? ? ? ? ? ? Override
? ? ? ? ? ?public void checkExit(int status) {
? ? ? ? ? ? ? ?throw new SecurityException( 不允许退出
? ? ? ? ? ?}
? ? ? ?});
? ? ? ?try {
? ? ? ? ? ?BufferedReader br new BufferedReader(new FileReader( file.txt
? ? ? ? ? ?System.out.println(br.readLine());
? ? ? ? ? ?br.close();
? ? ? ?} catch (IOException e) {
? ? ? ? ? ?System.exit(2);
? ? ? ?} finally {
? ? ? ? ? ?System.out.println( Exiting the program
? ? ? ?}
? ?}
}
四、总结
学习时一定要抱着不满足的心态 这样才能有机会学的更加深入 理解地更好。
Dubbo提供了六大核心能力 面向接口代理的高性能 RPC 调用、智能容错和负载均衡、...
GC作为Java知识体系里的一个面试热点 经常是众多程序猿常常需要复习的内容 借助...
作为一起新型贷款诈骗案件,相关监管部门和银行已关注到其中的欺诈风险,目前已...
简介 我们在做组合优化的时候需要去解决各种问题 根据问题的复杂度不同可以分为P...
注册了 域名 不备案可以吗?可以的。 注册域名 并不是一定要备案的,只有搭 建网...
随着云计算带动数据中心的发展,微服务器成为服务器市场上一个新的定制化方案,...
众所周知,大数据行业充斥着大量的专业词汇,很多人不一定都准确掌握和了解这些...
本文为2021年阿里云峰会 阿里云开发者大会大数据与AI一体化开发平台分论坛 如何...
1. 接口描述 接口请求域名: cvm.tencentcloudapi.com 。 本接口(DescribeInsta...
以下文章来源于信息通信技术与政策 ,作者董悦,李艺 等 1 引言 随着工业互联网...