热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java中finally关键字的几个坑

java中的很多人都认为运行代码输出:

java中的 finally 关键字通常与 try/catch 块一起使用。用来在方法结束前或发生异常时做一些资源释放的操作。虽然看起来很简单,在日常开发中也发现几个关于 finlla 问题。

finally 语句块一定会执行吗?

很多人都认为 finally 语句块是肯定要执行的,比如下面的代码,只要进入了 try/catch 块,不管有没有异常,都会执行 finllay 块 :

public static int test(){
        try {
            System.out.println("try block");
            int i = 1 / 0;
            return 0;
        } finally {
            System.out.println("finally block");
        }
    }

运行代码输出 :

try block
finally block
Exception in thread "main" java.lang.ArithmeticException: / by zero

但是回到这个问题,结果并不像大多人所认为的,答案是否定的,我们先来看下面这个例子:

public static int test(){
       try {
           System.out.println("try block");
           System.exit(0);
           return 0;
       } finally {
           System.out.println("finally block");
       }
   }

运行代码输出 :

try block

我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行, finally 语句块还是没有执行。

如果执行了finally,函数返回值问题

public static int test(){
        try {
            System.out.println("try block");
            int i = 1 / 0; // 注释
            return 0;
        } catch (Exception e) {
            System.out.println("catch block");
            return 1;
        } finally {
            System.out.println("finally block");
            return 2;
        }
    }

对于上面的代码,相信大部分人都能知道输出值是 2 ,打印结果也确实是 2 ,就算把 int i = 1 / 0 这一行注释掉,打印结果也是 2 。所以在这里我们可以下结论 : finally 里的 return 语句会把 try/catch 块里的 return 语句效果给覆盖掉。

假如我们不在 finallyreturn ,结果会怎样?我们再看看下面的例子 :

public static int test(){
       int i = 999;
       try {
           System.out.println("try block");
           i = 1 / 0;
           return i;
       } catch (Exception e) {
           System.out.println("catch block");
           i = 100;
           return i;
       } finally {
           System.out.println("finally block");
           i = 200;
       }
   }

打印结果是 :

try block
catch block
finally block
 100

虽然调用了 finllay 改变了i的值,但是最后输出还是 100 ,为什么呢?我们可以通过分析字节码文件得到结果 :

 public static int test();
    Code:
       0: sipush        999
       3: istore_0
。。。
      29: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      32: ldc           #14                 // String catch block
      34: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      37: bipush        100
      39: istore_0
      40: iload_0
      41: istore_2
      42: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      45: ldc           #12                 // String finally block
      47: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      50: sipush        200
      53: istore_0
      54: iload_2
      55: ireturn
。。。

从字节码文件中可以看出,在 37:39 中把 100 存入 index 0 的位置,就是设置 i 的值为 100 ,在 40:41 中把 index 0 的值赋值给了 index 2 的位置,在 50:53 中把 200 存入 index 0 的位置,就是设置 i 的值为 200 ,最后在 54:55 中把 index 2 的值加载出来并返回,即最后返回的是 index 2 的值 100

对于这种情况我的理解就是在 return 的的时候会把返回值压入栈,并把返回值赋值给栈中的局部变量, 最后把栈顶的变量值作为函数返回值。所以在 finally 中的返回值就会覆盖 try/catch中 的返回值,如果 finally 中不执行 return 语句,在 finally 中修改返回变量的值,不会影响返回结果。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 我们


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
author-avatar
潇洒嘉新
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有