`

从JVM指令层面看try-catch-finally返回值问题

阅读更多

从JVM指令层面看try-catch-finally返回值问题

 

貌似很多人对下面的方法的返回值都比较迷糊:

package cc.lixiaohui.demo;  
  
public class ReturnValueTest {  
    public int test() {  
        int a;  
        try {  
            a = 1;  
            //int b = 1 / 0;  
            return a;  
        } catch (Exception e) {  
            a = 2;  
            return a;  
        } finally {  
            a = 3;  
        }  
    }  
}

 

test方法的返回值自然是1,如果把注释那行去掉,那就是2.

 

为什么?

用javap -verbose ReturnValueTest 查看字节码:

重点查看test()方法指令:

Compiled from "ReturnValueTest.java"
public class cc.lixiaohui.demo.ReturnValueTest extends java.lang.Object
  SourceFile: "ReturnValueTest.java"
  minor version: 0
  major version: 49		
  Constant pool: 		--常量池
const #1 = class        #2;     //  cc/lixiaohui/demo/ReturnValueTest
const #2 = Asciz        cc/lixiaohui/demo/ReturnValueTest;
const #3 = class        #4;     //  java/lang/Object
const #4 = Asciz        java/lang/Object;
const #5 = Asciz        <init>;
const #6 = Asciz        ()V;
const #7 = Asciz        Code;
const #8 = Method       #3.#9;  //  java/lang/Object."<init>":()V
const #9 = NameAndType  #5:#6;//  "<init>":()V
const #10 = Asciz       LineNumberTable;
const #11 = Asciz       LocalVariableTable;
const #12 = Asciz       this;
const #13 = Asciz       Lcc/lixiaohui/demo/ReturnValueTest;;
const #14 = Asciz       test;
const #15 = Asciz       ()I;
const #16 = class       #17;    //  java/lang/Exception
const #17 = Asciz       java/lang/Exception;
const #18 = Asciz       a;
const #19 = Asciz       I;
const #20 = Asciz       e;
const #21 = Asciz       Ljava/lang/Exception;;
const #22 = Asciz       SourceFile;
const #23 = Asciz       ReturnValueTest.java;

{
public cc.lixiaohui.demo.ReturnValueTest();		--构造方法就不分析了
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 8: 0

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       Lcc/lixiaohui/demo/ReturnValueTest;


public int test();
  Code:
   Stack=1, Locals=5, Args_size=1 -- [Stack=1貌似表示栈深度为1,]Locals=5表示局部变量表长度为5, Args_size=1表示该方法有1个参数(this)
   0:   iconst_1		--将int值 1 压栈
   1:   istore_1		--将栈顶值(即1)弹出保存至局部变量表第2个位置(局部变量表下标是从0开始的,但是0位置被this变量占用了)
   2:   iload_1			--将局部变量表第2个位置的值压栈
   3:   istore  4		--将栈顶的值弹出并保存至局部变量表第5个位置(这里可以看到)
   5:   iconst_3		--(这里开始为finally块)将int值3压栈
   6:   istore_1		--将栈顶的值(即3)弹出并存储至局部变量表第2个位置
   7:   iload   4		--将局部变量表第5个位置(即1)压栈
   9:   ireturn			--返回栈顶的值(即1), 结束方法调用(该路径为try(未抛异常) -> finally)
   
   10:  astore_2		--将栈顶的引用(这里即为catch块中捕捉到的异常e)存储至局部变量表的第3个位置
   11:  iconst_2		--将int值2压栈
   12:  istore_1		--将栈顶值(即2)弹出并存储至局部变量表第2个位置
   13:  iload_1			--将局部变量表第2个位置的值(即2)压栈
   14:  istore  4		--将栈顶值弹出(即2)并保存至局部变量表第5个位置,原来第五个位置是1,现在1被覆盖了,变为2
   16:  iconst_3		--(这里开始为finally块)将int值3压栈
   17:  istore_1		--将栈顶的值(即3)弹出并存储至局部变量表第2个位置
   18:  iload   4		--将局部变量表第5个位置(即2)压栈
   20:  ireturn			--返回栈顶的值(即2),结束方法调用(该路径为try(抛Exception或其子类异常) -> catch -> finally)
   
   21:  astore_3		--将栈顶的引用(这里为非Exception的子类异常)存储至局部变量表的第4个位置
   22:  iconst_3		--将int值3压栈
   23:  istore_1		--将栈顶值(即3)弹出并存储至局部变量表第二个位置
   24:  aload_3			--将局部变量表第4个位置(即为异常引用)压栈
   25:  athrow			--将栈顶的异常抛出(该路径为try(抛非Exception或其子类异常) -> finally, 或者try(抛Exception或其子类异常) -> catch(抛任何异常) -> finally )
  Exception table:
   from   to  target type
     0     5    10   Class java/lang/Exception	--若执行到0-5行(即在try块中)抛出java.lang.Exception或其子类则跳转至第10行执行(即catch块)
     0     5    21   any						--若执行到0-5行(即在try块中)抛出非java.lang.Exception或其子类则跳转至第21行执行(即finally块)
    10    16    21   any						--若执行到10-16行(即在catch块中)抛出任何异常则跳转至21行执行(即finally块)
  LineNumberTable:		--这个表为源码行数与字节码行数的映射
   line 13: 0
   line 14: 2
   line 19: 5
   line 14: 7
   line 15: 10
   line 16: 11
   line 17: 13
   line 19: 16
   line 17: 18
   line 18: 21
   line 19: 22
   line 20: 24

  LocalVariableTable:		--这个即为局部变量表, start和length结合起来就可以确定该变量的作用范围, 例如this作用范围为整个方法
   Start  Length  Slot  Name   Signature
   0      26      0    this       Lcc/lixiaohui/demo/ReturnValueTest;	--占用第1个slot(一个slot应该是32bits)
   2      8      1    a       I											--I标识int, 占用第2个slot
   13      8      1    a       I										--占用第2个slot
   24      2      1    a       I										--占用第2个slot
   11      10      2    e       Ljava/lang/Exception;					--占用第3个slot


}

 

可以发现jvm始终把返回值放在最后一个局部变量表的位置,而且在finally中改变x并不影响返回值.

 

参考:

JVM栈帧

http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html

0
2
分享到:
评论

相关推荐

    Java异常处理

    Java异常处理是Java编程语言中的一个关键特性,它允许程序员优雅地处理程序运行时可能...通过合理地使用`try-catch-finally`,以及正确地处理`checked`和`unchecked`异常,开发者可以编写出更加安全、可靠的Java应用。

    第一阶段面试汇总.docx

    - 在 try-catch-finally 结构中,finally 块总是会执行,即使 return 语句在 try 或 catch 块中出现也会如此。 #### 四、Java 内存模型 1. **程序计数器(Program Counter Register)**: - 每个线程拥有一个独立的...

    java笔试题大全

    - 异常处理(try-catch-finally) 2. **面向对象**: - 类与对象的概念 - 封装、继承、多态的实现与应用 - 访问修饰符(public, private, protected, default) - 构造函数与this关键字 - static关键字的用法...

    深入理解java异常处理机制

    1. **`testEx2`** 方法中的 `finally` 块被执行后,返回了一个布尔值 `false`,这导致了 `testEx1` 方法中的 `finally` 块也输出了 `false` 的返回值,而不是在 `catch` 块中抛出异常。 2. **`testEx1`** 方法中的 ...

    java finally块执行时机全面分析

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

    学习java的第二个项目,面试技巧和基础进阶知识。.zip

    - 异常处理:理解如何使用try-catch-finally语句处理程序中的异常。 - 方法:了解方法的定义、参数传递和返回值。 2. **Java进阶主题**: - 集合框架:熟悉ArrayList、LinkedList、HashMap、HashSet等集合类的...

    Java异常面试题(2020最新版).docx

    7. 不要在finally块中使用return,以防止覆盖catch块的返回值。 8. 包装异常时保留原始异常信息,以便追踪问题来源。 9. 不要使用异常来控制程序流程,异常应该主要用于异常情况。 10. 使用标准异常,如`...

    Java 课件

    - 异常处理:try-catch-finally语句块,自定义异常。 5. **类和对象(JAVA-L4-ClassesAndObjects(1).ppt & JAVA-L5-ClassesAndObjects(2).ppt)** - 面向对象编程(OOP)基础:封装、继承、多态。 - 类的定义:...

    java复习题(选择填空及编程)

    例如,题目可能询问关于访问修饰符的区别(public、private、protected、默认),类的继承关系,异常处理的try-catch-finally语句块,ArrayList和LinkedList的区别,或者Socket编程的基本概念。 在Java的学习过程中...

    SCJP Java 考试指南(考试号310-065)

    1. **Java语言基础**:这是SCJP考试的核心部分,包括数据类型、变量、运算符、流程控制(如if-else、switch、for、while循环)、方法的定义和调用、异常处理(try-catch-finally语句)。 2. **类和对象**:深入理解...

    最新计算机等级考试二级java模拟题四.doc

    在Java中,一个try语句后面必须跟着至少一个catch块或finally块,或者两者都有。 **知识点5:获取异常堆栈信息** - **选项解析**: - A) fillinStack()方法:不存在此方法。 - B) printStackTrace()方法:用于...

    java 1-11章学习课件

    - try-catch-finally语句块 - 自定义异常类 - throws关键字的使用 9. **第九章:集合框架** - List接口(ArrayList, LinkedList)和Set接口(HashSet, TreeSet) - Map接口(HashMap, TreeMap, ...

    Java语言程序设计

    此外,掌握流程控制结构也是至关重要的,比如条件语句(if-else)、循环(for、while、do-while)以及异常处理(try-catch-finally)。 函数是Java编程的重要组成部分,它们封装了可重用的代码块。你需要了解函数的...

    2023年Java面试题(非常详细)

    - try-catch-finally:理解异常的捕获和处理。 - throws和throw:了解如何声明和抛出异常。 - 自定义异常:创建和使用自定义异常类。 6. **JVM内存模型** - 堆内存、栈内存、方法区:了解各区域的作用和垃圾...

    校招Java面试常见知识点

    - **异常处理**:try-catch-finally结构,以及throw和throws关键字。 - **字符串处理**:String类的特点,StringBuilder和StringBuffer的区别。 2. **Java进阶**: - **集合框架**:List、Set、Queue、Map接口...

    java面试题

    - try-catch-finally:知道如何编写异常处理代码,理解finally块的作用。 - 自定义异常:了解何时以及如何定义自定义异常。 4. **集合框架** - List、Set、Queue:理解这些接口的特性,知道常用的实现类,如...

    Java编程基础

    - try-catch-finally语句块。 - throw抛出异常。 - throws声明可能抛出的异常。 #### 六、Java集合框架 - **集合框架概述**: - Java集合框架提供了标准的数据结构实现。 - 主要接口包括List、Set、Map等。 - ...

    code Java核心技术第7版到第10版 随书源码

    - **异常处理**:try-catch-finally结构、自定义异常。 - **输入/输出流**:文件操作、对象序列化。 - **集合框架**:List、Set、Map接口及其实现类。 2. **Java核心技术卷II**:主要涉及Java的高级特性,包括:...

    Java基础讲义PPT

    - 异常的概念,try-catch-finally结构。 - 自定义异常与异常链。 11. **集合框架**: - List、Set、Map接口及其常见实现类如ArrayList、LinkedList、HashSet、HashMap等。 - 集合的遍历、添加、删除操作。 - ...

Global site tag (gtag.js) - Google Analytics