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

简介: Java 的 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) {

           // 省略一些代码

       } 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");

       }

   }

}
/


四、总结

学习时一定要抱着不满足的心态,这样才能有机会学的更加深入,理解地更好。




相关文章
|
1天前
|
JSON Java 数据挖掘
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
|
18天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
42 3
|
25天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
62 2
|
1月前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
84 5
|
1月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
62 5
|
2月前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####
|
1月前
|
安全 Java API
Java中的Lambda表达式:简化代码的现代魔法
在Java 8的发布中,Lambda表达式的引入无疑是一场编程范式的革命。它不仅让代码变得更加简洁,还使得函数式编程在Java中成为可能。本文将深入探讨Lambda表达式如何改变我们编写和维护Java代码的方式,以及它是如何提升我们编码效率的。
|
Java
java中finally和return的执行顺序
注意:return的位置。。。从这几个例子中可以看到,如果try之前没有有条件的return,则try..catch..finally语句块中的语句都是顺序执行(如果try中或者catch中 有return语句,那么先执行该return,然后执行finally, 如果finally中也有return, 该出的返回值会覆盖掉try 和 catch中的return值; 如果try.
852 0