- 源码
TestClass.java
public class TestClass { public int inc() { int x; try { x=1; return x; }catch (Exception e){ x=2; return x; }finally { x=3; } } }
- jdk1.7编译出来的
TestClass.class
的class文件结构
Classfile /root/IdeaProjects/java7_test/out/production/java7_test/org/fenixsoft/clazz/TestClass.class Last modified Feb 28, 2019; size 601 bytes MD5 checksum 495c52e30f4cf72096d5092696baf0b4 Compiled from "TestClass.java" public class org.fenixsoft.clazz.TestClass SourceFile: "TestClass.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4.#23; // java/lang/Object."<init>":()V #2 = Class #24; // java/lang/Exception #3 = Class #25; // org/fenixsoft/clazz/TestClass #4 = Class #26; // java/lang/Object #5 = Utf8 <init>; #6 = Utf8 ()V; #7 = Utf8 Code; #8 = Utf8 LineNumberTable; #9 = Utf8 LocalVariableTable; #10 = Utf8 this; #11 = Utf8 Lorg/fenixsoft/clazz/TestClass;; #12 = Utf8 inc; #13 = Utf8 ()I; #14 = Utf8 x; #15 = Utf8 I; #16 = Utf8 e; #17 = Utf8 Ljava/lang/Exception;; #18 = Utf8 StackMapTable; #19 = Class #24; // java/lang/Exception #20 = Class #27; // java/lang/Throwable #21 = Utf8 SourceFile; #22 = Utf8 TestClass.java; #23 = NameAndType #5:#6; // "<init>":()V #24 = Utf8 java/lang/Exception; #25 = Utf8 org/fenixsoft/clazz/TestClass; #26 = Utf8 java/lang/Object; #27 = Utf8 java/lang/Throwable; { public org.fenixsoft.clazz.TestClass(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1; // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lorg/fenixsoft/clazz/TestClass; public int inc(); flags: ACC_PUBLIC Code: stack=1, locals=5, args_size=1 0: iconst_1 1: istore_1 2: iload_1 3: istore_2 4: iconst_3 5: istore_1 6: iload_2 7: ireturn 8: astore_2 9: iconst_2 10: istore_1 11: iload_1 12: istore_3 13: iconst_3 14: istore_1 15: iload_3 16: ireturn 17: astore 4 19: iconst_3 20: istore_1 21: aload 4 23: athrow Exception table: from to target type 0 4 8 Class java/lang/Exception 0 4 17 any 8 13 17 any 17 19 17 any LineNumberTable: line 8: 0 line 9: 2 line 14: 4 line 10: 8 line 11: 9 line 12: 11 line 14: 13 LocalVariableTable: Start Length Slot Name Signature 2 6 1 x I 9 8 2 e Ljava/lang/Exception; 11 6 1 x I 0 24 0 this Lorg/fenixsoft/clazz/TestClass; 21 3 1 x I StackMapTable: number_of_entries = 2 frame_type = 72 /* same_locals_1_stack_item */ stack = [ class java/lang/Exception ] frame_type = 72 /* same_locals_1_stack_item */ stack = [ class java/lang/Throwable ] }
- 步骤说明表
说明1:由于
栈深度(max_stack,其实就是最大Slot数)
为1(),所以用1个中括号
表示,栈顶是最右边;最大本地变量(max_locals,单位也是Slot)
为5,所以用5个中括号表示,排序从左到右从0开始。
说明2:由于本地变量是有作用域的,所以作用域之外本地变量会空着
说明3:局部变量表中第0个Slot放的L是this(即本方法所属对象的引用)
说明4:如果不确定Slot中是否有值,那么会用?表示。
指令行号 | 指令 | 说明 | 本条指令执行完之后栈 | 本条指令执行完之后本地变量表 | 本条指令执行完之后本地变量 | 备注 |
---|---|---|---|---|---|---|
这是还没执行指令的状态 | [] | [L][][][][] | this | |||
0 | iconst_1 | 把int类型值1压入栈顶 | [1] | [L][][][][] | this | |
1 | istore_1 | 弹出栈顶的int类型放入本地变量表第1个Slot | [] | [L][1][][][] | this | 即x=1 |
2 | iload_1 | 把本地变量表的第一个Slot 中的int类型的值压入栈顶 |
[1] | [L][1][][][] | this,x | 根据1、2发现,x=1这条代码并不是x的作用域,也就是说一个变量的作用域不是它被赋值的时候。根据测试得知一般赋值的下一行代码就是作用域的开始 |
3 | istore_2 | 弹出栈顶的int类型放入局部变量表的第二个Slot 中 |
[] | [L][1][1][][] | this,x | 根据1、3可知局部变量表被使用的时候,并不一定需要变量名:1中第一个Slot没有变量名,但它有值;2中第二个Slot没有变量名,但它有值。这里的第二个Slot是临时存放return返回值 的,它是有可能被其他局部变量覆盖 的。 |
4 | iconst_3 | 把int类型值3压入栈中 | [3] | [L][1][1][][] | this,x | |
5 | istore_1 | 弹出栈顶int类型存入本地变量表第一个Slot | [] | [L][3][1][][] | this,x | 即x=3 |
6 | iload_2 | 把本地变量表第二个Slot压入栈中 | [1] | [L][3][1][][] | this,x | |
7 | ireturn | 弹出栈顶int类型并存入返回值,返回方法 | [] | [L][3][1][][] | this,x | 即return x
|
从8开始是catch 的代码,首先会给Exception e 变量赋值,本步骤就是把异常引用压入栈顶,后面L代表引用 |
[L] | [L][?][?][][] | ||||
8 | astore_2 | 把栈顶引用存入局部变量表第二个Slot | [] | [L][?][L][][] | this | 即Exception e=? ,因为catch 里面不需要第二个Slot中临时存放的return返回值 了,所以直接覆盖了 |
9 | iconst_2 | 把int类型值2压入栈 | [2] | [L][?][L][][] | this,e | |
10 | istore_1 | 弹出栈顶int类型存入局部变量表第一个Slot | [L] | [L][2][L][][] | this,e | 即x=2
|
11 | iload_1 | 把第一个Slot中的int类型压入栈 | [2] | [L][2][L][][] | this,e,x | |
12 | istore_3 | 弹出栈顶元素放入局部变量表第三个Slot中 | [] | [L][2][L][2][] | this,e,x | 第三个Slot临时存放return返回值
|
13 | iconst_3 | 把int类型值3压入栈 | [3] | [L][2][L][2][] | this,e,x | |
14 | istore_1 | 弹出栈顶的int类型存入局部变量表第一个Slot | [] | [L][3][L][2][] | this,e,x | 即x=3 |
15 | iload_3 | 把第三个Slot中的int类型压入栈 | [2] | [L][3][L][2][] | this,e,x | |
16 | ireturn | 弹出栈顶int类型并存入返回值,返回方法 | [] | [L][3][L][2][] | this,e,x | 即return x
|
从17开始是除了Exception或其子类 的其他异常,即上面的any,本步骤就是把异常引用压入栈顶 |
[L] | [L][?][?][?][] | this | |||
17 | astore 4 | 弹出栈顶引用类型存入局部变量表第四个Slot | [] | [L][?][?][?][L] | this | 这里并没有 把第三个Slot中的临时return返回值 覆盖,难道是因为它们都是没名字的变量? |
19 | iconst_3 | 把int类型值3压入栈顶 | [3] | [L][?][?][?][L] | this | |
20 | istore_1 | 弹出栈顶int类型存入本地变量表第一个Slot | [] | [L][?][?][?][L] | this,x | 即x=3 |
21 | aload 4 | 把第四个Slot中的引用压入栈 | [L] | [L][?][?][?][L] | this,x | |
23 | athrow | 抛出异常 | 根据21、23,这就是把异常压入栈抛给更高一级了 |
-
Exception table
说明
根据java class.类结构.method中
异常表结构
可知:
try
中出现Exception或其子类
的异常
,就转到catch
中处理
try
中出现其他异常,就转入finally
处理
catch
中出现任何异常(包括Exception或其子类
),转入finally处理
最后一个可以不管