前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 的 finally 代码块的代码一定会执行吗?

Java 的 finally 代码块的代码一定会执行吗?

作者头像
明明如月学长
发布2021-08-31 14:59:50
4120
发布2021-08-31 14:59:50
举报
文章被收录于专栏:明明如月的技术专栏

一、背景

对于很多初学者而言,会想当然地认为 “finally 代码块一定会被执行”,因此我们可以看下面这个案例:

代码语言:javascript
复制
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呢?

于是我们想到,如果让虚拟机退出,问题不就解决了吗? (就是这么暴力)

因此填充代码:

代码语言:javascript
复制
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 的源码如下:

代码语言:javascript
复制
    /**
     * Terminates the currently running Java Virtual Machine. The
     * argument serves as a status code; by convention, a nonzero status
     * code indicates abnormal termination.
     * 
     * This method calls the exit method in class
     * Runtime. This method never returns normally.
     * 
     * The call System.exit(n) is effectively equivalent to
     * the call:
     *      * Runtime.getRuntime().exit(n)
     * 
     *
     * @param      status   exit status.
     * @throws  SecurityException
     *        if a security manager exists and its checkExit
     *        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:

代码语言:javascript
复制
    /**
     * 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.
     *
     *  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}.
     *
     *  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.
     *
     *  The {@link System#exit(int) System.exit} method is the
     * conventional and convenient means of invoking this method. 
     *
     * @param  status
     *         Termination status.  By convention, a nonzero status code
     *         indicates abnormal termination.
     *
     * @throws SecurityException
     *         If a security manager is present and its {@link
     *         SecurityManager#checkExit checkExit} 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);
    }

三、延伸

同样的问题,请看下面代码片段:

代码语言:javascript
复制
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 ,则 会进行安全检查。

代码语言:javascript
复制
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代码块。

代码语言:javascript
复制
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");
        }
    }
}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/10/08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、分析
  • 三、延伸
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档