《深入Java虚拟机学习笔记》- 第18章 finally子句
本章主要介绍字节码实现的finally子句。包括相关指令以及这些指令的使用方式。此外,本章还介绍了Java源代码中finally子句所展示的一些令人惊讶的特性,并从字节码角度对这些特征进行了解释。
1、微型子例程
字节码中的finally子句表现的很像“微型子例程”。Java虚拟机在每个try语句块和与其相关的catch子句的结尾处都会“调用”finally子句的子例程。finally子句结束后(这里的结束指的是finally子句中最后一条语句正常执行完毕,不包括抛出异常,或执行return、continue、break等情况),隶属于这个finally子句的微型子例程执行“返回”操作。程序在第一次调用微型子例程的地方继续执行后面的语句。
Java方法与微型子例程使用不同的指令集。跳转到微型子例程的指令是jsr或者jsr_w,将返回地址压入栈。执行完毕后调用ret指令。ret指令并不会从栈中弹出返回地址,而是在子例程开始的时候将返回地址从栈顶取出存储在局部变量,ret指令从局部变量中取出。这是因为finally子句本身会抛出异常或者含有return、break、continue等语句。finally确保会执行到,即使try或者catch中有return等语句。
先看看下面的一道面试题:
int normal(){ try{ return 10; }finally{ return 20; } }
public class TryFinallyTest{ private static int normal(){ try{ return 10; }finally{ return 20; } } public static void main(String args[]){ System.out.println(normal()); } }
Compiled from "TryFinallyTest.java" public class TryFinallyTest extends java.lang.Object{ public TryFinallyTest(); Code: 0:aload_0 1:invokespecial#1; //Method java/lang/Object."<init>":()V 4:return private static int normal(); Code: 0:bipush10 2:istore_1 3:bipush20 5:ireturn 6:astore_2 7:bipush20 9:ireturn Exception table: from to target type 0 3 6 any 6 7 6 any //0-2 对应try子句,“3-5”及"7-9"表示finally子例程,而6表示编译器生成的catch处理过程的开端(这可以通过加粗的异常表看到)。 //注意我们没有看到关于return 10的任何信息,该语句已经被忽略了。 public static void main(java.lang.String[]); Code: 0:getstatic#2; //Field java/lang/System.out:Ljava/io/PrintStream; 3:invokestatic#3; //Method normal:()I 6:invokevirtual#4; //Method java/io/PrintStream.println:(I)V 9:return }
class Surprise { static boolean surpriseTheProgrammer(boolean bVal) { while (bVal) { try { return true; } finally { break; } } return false; } }
Compiled from "Surprise.java" class Surprise extends java.lang.Object{ Surprise(); Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return static boolean surpriseTheProgrammer(boolean); Code: 0: iload_0 1: ifeq 8 4: goto 8 7: pop 8: iconst_0 9: ireturn Exception table: from to target type 4 7 7 any }
private static int normal() { int a; try { a = 1; System.out.println("in try:set a to " + a); return a; } finally { a = 2; System.out.println("in finally:set a to " + a); } }
in try:set a to 1 in finally:set a to 2 1
private static int normal(); Code: 0:iconst_1 1:istore_0 //对应a=1(现在栈中压入常量1,再弹出存储到局部变量_0即a中) 2:getstatic#2; //Field java/lang/System.out:Ljava/io/PrintStream; 5:new#3; //class java/lang/StringBuilder 8:dup 9:invokespecial#4; //Method java/lang/StringBuilder."<init>":()V 12:ldc#5; //String in try:set a to 14:invokevirtual#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17:iload_0 18:invokevirtual#7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 21:invokevirtual#8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24:invokevirtual#9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V //9-24对应try块中的打印语句 27:iload_0 28:istore_1 //令人困惑的原因在这里-----编译器在try块中把变量a的值存在了标号为1的局部变量中,再跳到语句56我们就明白了,try中返回的是标号1的局部变量的值,而不是修改过的值。 29:iconst_2 30:istore_0 31:getstatic#2; //Field java/lang/System.out:Ljava/io/PrintStream; 34:new#3; //class java/lang/StringBuilder 37:dup 38:invokespecial#4; //Method java/lang/StringBuilder."<init>":()V 41:ldc#10; //String in finally:set a to 43:invokevirtual#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 46:iload_0 47:invokevirtual#7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 50:invokevirtual#8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 53:invokevirtual#9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V //29-53对应finally块 56:iload_1 57:ireturn //56-57对应try中的return a. 58:astore_2 59:iconst_2 60:istore_0 //59-60对应finally中的a=2; 61:getstatic#2; //Field java/lang/System.out:Ljava/io/PrintStream; 64:new#3; //class java/lang/StringBuilder 67:dup 68:invokespecial#4; //Method java/lang/StringBuilder."<init>":()V 71:ldc#10; //String in finally:set a to 73:invokevirtual#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 76:iload_0 77:invokevirtual#7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 80:invokevirtual#8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 83:invokevirtual#9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 86:aload_2 87:athrow Exception table: from to target type 0 29 58 any 58 59 58 any
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。