JVM系列_从JVM角度分析finally块中程序到底在return之前执行还是return之后执行

对于finally的作用相关内容不再陈述,网上有很多

本博文着重分析finally块中程序到底在return之前执行还是return之后执行?

没有了解过JVM指令的可以看这篇博客:JVM操作指令集解析

先看一个程序示例:

public class AtomicDemo{
	public static int test(int i){
		try {
			i=9;
			return i;
		} finally  {
			i=10;
		}
	}
	public static void main(String[] args) {
		System.out.println(test(12));
	}
}

输出结果:

9

分析:

  1. 仅从控制台输出结果上看好像是在return之后执行, 因为i的值是9, 没有被修改为10;
  2. 但是, 事实上是在return之前执行的, 并且在finally块执行结束之后i的值是已经被修改为10了
  3. 可以这样理解:return 语句1; finally块是在语句1执行之后, return执行之前执行的;

反编译后的结果:

Compiled from "AtomicDemo.java"
public class com.lic.atomic.AtomicDemo {
  public com.lic.atomic.AtomicDemo();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static int test();
    Code:
       0: bipush        9 			//将一个8位带符号整数压入栈    (将int类型常量9放入操作数栈中)
       2: istore_0					//将int类型值存入局部变量0     (将局部变量0放入局部变量表,然后将int类型常量9从操作数栈中取出,存入局部变量0中)
       3: iload_0					//从局部变量0中装载int类型值   (将局部变量0中的值9压入操作数栈,此步骤是准备将i(9)返回 --> return i; 需要先将9压入操作数栈,然后返回) 
      
   	   4: istore_2					//将int类型值存入局部变量2	 (将上一步中已经加载到操作栈中准备返回的值9暂存于局部变量2中; 之所以是暂存,是因为在finally块执行完之后,会将局部变量2中的值返回)
       5: bipush        10			//将一个8位带符号整数压入栈    (将int类型常量10放入操作数栈中)
       7: istore_0					//将int类型值存入局部变量0     (将局部变量0放入局部变量表,然后将int类型常量10从操作数栈中取出,存入局部变量0中; 此时i的值已经被修改为10)
       
	   8: iload_2					//从局部变量2中装载int类型值   (将局部变量2中的值9压入操作数栈) 
       9: ireturn					//从方法中返回int类型的数据    (将操作数栈栈顶元素弹出,返回)
     
	  10: astore_1					//将引用类型或returnAddress类型值存入局部变量1
      11: bipush        10			//将一个8位带符号整数压入栈 
      13: istore_0					//将int类型值存入局部变量0
      14: aload_1					//从局部变量1中装载引用类型值
      15: athrow					//抛出异常或错误 
    Exception table:
       from    to  target type
           0     5    10   any

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #29                 // Method test:()I
       6: invokevirtual #31                 // Method java/io/PrintStream.println:(I)V
       9: return
}

分析:

  1. 在步骤3中, 将 i 的值 9 压入操作数栈,准备返回; 然后发现需要执行finally块中的数据, 因此jvm将准备返回的值 9 存入局部变量2中, 接下来去执行finally中的程序, 将 i 的值修改为10;
  2. 在finally块中的程序执行结束之后将局部变量2中的数据重新压入操作数栈中, 然后返回, 所以返回的值为 9 ;
  3. 但是此时 i 的值已经被修改为10
如果在finally块中被修改的数据时一个对象会怎么样呢?

程序示例:

public class AtomicDemo3 {
	public static StringBuffer test(){
		StringBuffer sb = new StringBuffer();
		try {
			sb.append("abc");
			return sb;
		} finally  {
			sb.append("123");
		}
	}
	
	public static void main(String[] args) {
		System.out.println(test().toString());
	}
}

输出结果:

abc123

反编译结果:

Compiled from "AtomicDemo3.java"
public class com.lic.atomic.AtomicDemo3 {
  public com.lic.atomic.AtomicDemo3();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static java.lang.StringBuffer test();
    Code:
       0: new           #16                 // class java/lang/StringBuffer
       3: dup
       4: invokespecial #18                 // Method java/lang/StringBuffer."<init>":()V
       7: astore_0
       8: aload_0
       9: ldc           #19                 // String abc
      11: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
      14: pop
      15: aload_0							//从局部变量0中装载引用类型值 (将对象sb的地址引用加载到操作栈准备返回)
     
      16: astore_2							//将引用类型或returnAddress类型值存入局部变量2 (将对象sb的地址引用暂存于局部变量2中,等finally中的程序执行结束之后返回)
      17: aload_0							//从局部变量0中装载引用类型值 (将对象sb的地址引用加载到操作栈中准备执行append()方法)
      18: ldc           #25                 // String 123
      20: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
      23: pop
      24: aload_2							//从局部变量2中装载引用类型值 (局部变量2中的对象sb的地址引用加载到操作栈准备返回)
      25: areturn							//从方法中返回引用类型的数据
    
	  26: astore_1
      27: aload_0
      28: ldc           #25                 // String 123
      30: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
      33: pop
      34: aload_1
      35: athrow
    Exception table:
       from    to  target type
           8    17    26   any

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #34                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #40                 // Method test:()Ljava/lang/StringBuffer;
       6: invokevirtual #42                 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
       9: invokevirtual #46                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: return
}

总结:

  1. finally块中的程序是在return之前执行的;
  2. 对于基本类型, 返回的结果是之前执行finally块程序之前的,因为jvm将执行finally块之前要返回的数据暂存于另一个变量B中了, 等执行finally块之后再将B中的值加载到操作数栈中返回;
  3. 对于对象类型, 在执行finally块前后返回的都是该对象的地址引用, 所以可以看到finally块执行后的结果;
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值