`

深入java虚拟机 finally,return问题

阅读更多

刚开始学习java的时候,finally,return是个头疼的问题,面试题里天天都是问谁先执行?

下面我们就结合《深入java虚拟机》第18章 finally子语句和例子对应的字节码来探讨一下这个问题

   看此篇文章之前可以看看前面一篇文章描述finally字节码:

http://abc08010051.iteye.com/admin/blogs/2154981

例子1:

    public static int tt1() {
        int b = 23;
        try {
            System.out.println("yes");
            return b += 88;
        } catch (Exception e) {
            System.out.println("error      :      " + e);
        } finally {
            if (b > 25) {
                System.out.println("b>25      :      " + b);
            }
            System.out.println("finally");
        }
        return b;
    }

   代码执行结果:

  

yes
b>25      :      111
finally
111

    bytecode:

   

bipush 23 //把23压入栈
istore_0  //把23放到位置为0的局部变量中
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "yes" //把常量“yes”压入栈
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //打印"yes"
iinc 0 88 //位置为0的变量值加88
iload_0   //把位置为0的局部变量(值为111)压入栈
istore_1  //把栈顶元素出栈,(栈顶元素为111),并赋值给位置为1的局部变量
iload_0  //把位置为0的局部变量值压入栈
bipush 25 //把25压入栈 
if_icmple 22  //比较栈顶两个int类型的值,如果小于0调到22执行
getstatic java/lang/System/out Ljava/io/PrintStream; //此行到22行,通过StringBuilder构建字符串"b>25      :      "加上b(即位置为0的局部变量值),并打印字符串
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //从11行到此行,通过StringBuilder构建字符串"b>25      :      "加上b(即位置为0的局部变量值),并打印字符串
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //打印“finally”字符串
iload_1 //获取位置为1的局部变量的值并压入栈中
ireturn //返回栈顶的int值
astore_1
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "error      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
aload_1
invokevirtual java/lang/StringBuilder/append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
iload_0
bipush 25
if_icmple 51
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
goto 74
astore_2
iload_0
bipush 25
if_icmple 69
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
aload_2
athrow
iload_0
ireturn//对应代码最后的return

    上面的字节码值解释了我们方法正常执行的部分,由上面的字节码解释我们知道:return语句时在finally例程执行之后才执行的

  

  例子2:

       这个例子和上面的那个差不多,差别是在finally语句块里对变量进行了操作:

 

    public static int tt2() {
        int b = 23;
        try {
            System.out.println("yes");
            return b += 88;
        } catch (Exception e) {
            System.out.println("error      :      " + e);
        } finally {
            if(b>25)
            {
                System.out.println("b>25      :      "+b);
            }
            System.out.println("finally");
            b = 100;
        }
        return b;
    }
   执行结果:

 

  

yes
b>25      :      111
finally
111
    这个结果很令人诧异,如果按照例子1得出来的结论,return 在finally语句块之后执行,那么输出结果应该是:

 

  

yes
b>25      :      111
finally
100

    这是为什么呢?

    我们来看看tt2()方法的字节码:

   

bipush 23 //把23压入栈
istore_0  //把23放到位置为0的局部变量中
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "yes" //把常量“yes”压入栈
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //打印"yes"
iinc 0 88 //位置为0的变量值加88
iload_0   //把位置为0的局部变量(值为111)压入栈
istore_1  //把栈顶元素出栈,(栈顶元素为111),并赋值给位置为1的局部变量
iload_0  //把位置为0的局部变量值压入栈
bipush 25 //把25压入栈 
if_icmple 22  //比较栈顶两个int类型的值,如果小于0调到22执行
getstatic java/lang/System/out Ljava/io/PrintStream; //此行到22行,通过StringBuilder构建字符串"b>25      :      "加上b(即位置为0的局部变量值),并打印字符串
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //从11行到此行,通过StringBuilder构建字符串"b>25      :      "加上b(即位置为0的局部变量值),并打印字符串
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //打印“finally”字符串
bipush 100 //把100压入栈顶
istore_0   //把栈顶值(100)弹出栈,并赋值给位置为0的局部变量(代码中的b)
iload_1  //把位置为1的局部变量(111)压入栈
ireturn  //弹出栈顶的int值,返回,退出方法
astore_1
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "error      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
aload_1
invokevirtual java/lang/StringBuilder/append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
iload_0
bipush 25
if_icmple 53
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
bipush 100
istore_0
goto 80
astore_2
iload_0
bipush 25
if_icmple 73
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
bipush 100
istore_0
aload_2
athrow
iload_0
ireturn

    从上面的字节码可以看出,return还是在finally语句块之后执行, 返回111而不是100的原因可以参考上面标注为红色的bytecode解释,

    当代码执行到“return b += 88;” 这一行时,jvm把局部变量b加88,加后的局部变量b存放在位置为0的变量中,然后把b的值复制存放到位置为1的局部变量中,

执行finally语句块时,对局部变量b进行操作,在虚拟机内部只是对位置为0的变量进行操作,当返回时,虚拟机返回的是位置为1的变量值,即为111

   深入java虚拟机也有此解释:

  

 

 

  例子3:

    

    public static int tt3() {
        int b = 23;
        try {
            System.out.println("yes");
            return b += 88;
        } catch (Exception e) {
            System.out.println("error      :      " + e);
        } finally {
            if(b>25)
            {
                System.out.println("b>25      :      "+b);
            }
            System.out.println("finally");
            b = 100;
            return b;
        }
    }
    执行结果:

 

  

yes
b>25      :      111
finally
100
    根据结果判断,在执行“return b += 88;”这行代码的时候,把表达式“b += 88;”执行以后去执行finally语句块执行,在finally语句中return,退出方法

 

   bytecode:
  
bipush 23 //把23压入栈
istore_0  //把23放到位置为0的局部变量中
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "yes" //把常量“yes”压入栈
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //打印"yes"
iinc 0 88 //位置为0的变量值加88
iload_0   //把位置为0的局部变量(值为111)压入栈
istore_1  //把栈顶元素出栈,(栈顶元素为111),并赋值给位置为1的局部变量
iload_0  //把位置为0的局部变量值压入栈
bipush 25 //把25压入栈 
if_icmple 22  //比较栈顶两个int类型的值,如果小于0调到22执行
getstatic java/lang/System/out Ljava/io/PrintStream; //此行到22行,通过StringBuilder构建字符串"b>25      :      "加上b(即位置为0的局部变量值),并打印字符串
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //从11行到此行,通过StringBuilder构建字符串"b>25      :      "加上b(即位置为0的局部变量值),并打印字符串
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V //打印“finally”字符串
bipush 100 //把100压入栈
istore_0  //把栈顶int值出栈,把值放到位置为0(局部变量b)的变量中
iload_0  //把位置为0的变量压入栈
ireturn  //把栈顶int值弹出返回,退出方法
astore_1
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "error      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
aload_1
invokevirtual java/lang/StringBuilder/append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
iload_0
bipush 25
if_icmple 53
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
bipush 100
istore_0
iload_0
ireturn
astore_2
iload_0
bipush 25
if_icmple 74
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder/<init>()V
ldc "b>25      :      "
invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
iload_0
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "finally"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
bipush 100
istore_0
iload_0
ireturn
    由上面的字节码可以看出:在finally语句块的return语句上退出的方法
    针对finally语句块中使用return,break, continue,以及抛出异常,深入java虚拟机上有一段描述:
  

 

    在jdk 1.6, 1.7的编译环境下已经看不到深入java虚拟上所说的jsr,ret指令了,但是不对称的调用和返回这个原则肯定还是存在的,不然的话jdk版本不能对上兼容,从代码执行效果来说,如果try语句块包含return语句,在finally语句块中包含return, continue,break或抛出异常,依finally语句块中的代码执行效果为准(可能执行不到try语句中的return语句)
   根据以上的现象,首先执行的是,return返回表达式,把结算的结果缓存,并且没有直接return退出方法,然后是执行finally语句块,如果finally语句块有return直接返回,否者执行完finally语句块之后,return前面的缓存结果;
   可以得出finally语句块时在try语句块中的return之前执行的;try中的return在执行finally语句块之前会“缓存”返回结果
  • 大小: 205.7 KB
  • 大小: 199.5 KB
  • 大小: 98.8 KB
分享到:
评论

相关推荐

    深入java虚拟机(inside the java virtual machine)

    java虚拟机的运行机理的详细介绍 Inside the Java Virtual Machine Bill Venners $39.95 0-07-913248-0 Inside the Java Virtual Machine Acknowledgments Introduction Part One: Java's Architecture 1 ...

    深入java虚拟机(八)开发自己的类加载器 1

    【深入Java虚拟机(八)开发自己的类加载器 1】 在Java编程中,类加载器(ClassLoader)是Java虚拟机(JVM)的核心组成部分,它负责将类的字节码加载到JVM中并转化为Java类。通常,Java应用程序使用系统默认的类...

    Java中finally和return的关系实例解析

    System.exit(0)语句是Java语言中的一种特殊语句,它用于终止Java虚拟机(JVM)的执行。如果在try块中执行了System.exit(0)语句,finally语句将不会被执行,因为JVM已经停止了执行。 Java中finally和return的关系...

    java虚拟机

    ### Java虚拟机(JVM)详解 #### 一、Java虚拟机概述 Java虚拟机(JVM)是一个抽象概念,它可以指代三个不同的概念: 1. **抽象规范**:JVM规范定义了Java虚拟机的行为和结构,是所有JVM实现的基础。 2. **具体实现**...

    深入理解Java虚拟机体系结构

    【深入理解Java虚拟机体系结构】 Java虚拟机(Java Virtual Machine,简称JVM)是Java平台的核心组成部分,它使得Java程序具有平台无关性,保证了“一次编译,到处运行”的特性。Java虚拟机主要负责加载类文件并...

    关于Java中try finally return语句的执行顺序浅析

    例如,在try语句块中执行了System.exit(0)语句,终止了Java虚拟机的运行,finally语句块也不会执行。 下面是一个示例代码Demo2: public class Test { public static void main(String[] args) { System.out....

    java大厂面经、直击BAT

    ## 二.finally finally 一定会被执行,如果 finally ...finalize()方法是Object类的一个protected方法,它是在对象被垃圾回收之前由Java虚拟机来调用的。 ## 四. finally到底是在return之前执行还是return之后执行?

    Java软件开发实战 Java基础与案例开发详解 10-5 finally关键字 共7页.pdf

    需要注意的是,只有在Java虚拟机(JVM)被强制关闭的情况下,`finally`块才不会被执行。此外,无论`try`块是以何种方式结束(正常结束、异常结束、通过`return`或`break`等控制流语句结束),`finally`块总是恰好在...

    完全解析Java编程中finally语句的执行原理

    清单2的案例展示了这种情况,`try`块内调用了`System.exit(0)`,这会导致Java虚拟机(JVM)立即停止运行,因此`finally`块无法执行。这种情况虽然在常规应用中不常见,但确实存在。 此外,当线程在执行`try`或`...

    java finally块执行时机全面分析

    关于字节码层面的解释,根据《深入Java虚拟机》一书的描述,`finally`块的实现是通过`jsr`(Jump to Subroutine,跳转到子例程)指令来实现的。这个指令使得JVM能够调转到`finally`块执行,然后在`finally`块执行...

    java程序设计基础

    - **跨平台**:Java程序可以在不同的操作系统上运行,如Windows、Linux、Mac OS等,这得益于Java虚拟机的存在。 - **可移植性**:Java程序不依赖于硬件,只要安装了相应的Java虚拟机,就可以在任何平台上运行。 -...

    Java软件开发实战 Java基础与案例开发详解 2-4 java类库组织结构和文档 共9页.pdf

    - **Java SE 体系结构**:Java SE 平台包括了Java类库、Java虚拟机(JVM)以及一系列标准APIs等关键组成部分,构成了Java程序运行的基础环境。 ### 2. Java 类库组织结构 - **JDK提供的预定义类**:JDK中包含了丰富...

    java1.6中文说明文档

    8. **增强的异常处理**:1.6引入了`finally`块中的`return`语句,允许在`finally`块中返回值,解决了某些情况下无法返回值的问题。 9. **动态代理**:Java 1.6增强了动态代理机制,允许在运行时创建代理类,动态...

    java工程师面试题总结

    Java工程师面试题总结涉及到Java基础的多个方面,包括语法、类、内部类、继承、异常处理、线程、集合、IO操作以及Java虚拟机的相关知识点。下面将对上述提及的知识点进行详细解释。 首先,Java基础部分包括: - ...

    Java 基础 第1阶段:基本语法-尚硅谷学习笔记(含面试题) 2023年

    JDK包含了编译Java源代码所需的Java编译器(javac)、运行Java程序的Java虚拟机(JVM)以及一系列开发工具。确保正确设置JAVA_HOME环境变量,以便在命令行中使用Java命令。 二、Java程序结构 Java程序由类(class)...

    Java基础+Android面试题

    10. Java虚拟机:讨论了对象的创建、内存布局、访问定位,以及JVM内存区域、内存模型、垃圾回收机制等。 11. Java类加载机制:了解类加载器的工作方式,类的加载过程和双亲委派模型。 Android基础知识: 1. ...

    Java程序员面试问题及详细解答

    "Java程序员面试问题及详细解答" 本文档旨在提供 Java 程序员面试的详细解答,涵盖了 Java 语言的基础知识、面向对象编程、多态、重载、重写、抽象类、接口、构造器、不可变对象、值传递、引用传递、哈希码、字符串...

    Java基础知识面试题(2020最新版).pdf

    Java的基础知识包括JVM(Java虚拟机)、JRE(Java运行环境)和JDK(Java开发工具包)的关系。JDK是开发Java程序的软件开发包,包含了JRE和开发工具。JRE是运行Java程序所需环境的集合,而JVM是实现Java跨平台特性的...

Global site tag (gtag.js) - Google Analytics