字节码分析-异常处理
源码
package java.bytecode;
/**
* 异常处理示例 - 演示try-catch-finally、throws声明
*/
public class ExceptionExample {
// try-catch-finally示例
public void exceptionExample() {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Caught: " + e.getMessage());
} catch (Exception e) {
System.out.println("Caught general exception");
} finally {
System.out.println("Finally block");
}
}
// throws声明示例
public void exceptionThrows() throws IllegalArgumentException {
throw new IllegalArgumentException("Test exception");
}
// 多个catch块示例
public void multipleCatchExample() {
try {
String str = null;
int length = str.length();
} catch (NullPointerException e) {
System.out.println("NullPointerException: " + e.getMessage());
} catch (Exception e) {
System.out.println("General exception: " + e.getMessage());
}
}
}
结构化反解析
Classfile /D:/zcp/github/yggdrasil/java/bytecode/ExceptionExample.class
Last modified 2025-12-3; size 1444 bytes
MD5 checksum c22fa3ad65cee9ae9957381d909600ba
Compiled from "ExceptionExample.java"
public class java.bytecode.ExceptionExample
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5.#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Fieldref #8.#9 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Class #10 // java/lang/System
#9 = NameAndType #11:#12 // out:Ljava/io/PrintStream;
#10 = Utf8 java/lang/System
#11 = Utf8 out
#12 = Utf8 Ljava/io/PrintStream;
#13 = String #14 // Finally block
#14 = Utf8 Finally block
#15 = Methodref #16.#17 // java/io/PrintStream.println:(Ljava/lang/String;)V
#16 = Class #18 // java/io/PrintStream
#17 = NameAndType #19:#20 // println:(Ljava/lang/String;)V
#18 = Utf8 java/io/PrintStream
#19 = Utf8 println
#20 = Utf8 (Ljava/lang/String;)V
#21 = Class #22 // java/lang/ArithmeticException
#22 = Utf8 java/lang/ArithmeticException
#23 = Class #24 // java/lang/StringBuilder
#24 = Utf8 java/lang/StringBuilder
#25 = Methodref #23.#3 // java/lang/StringBuilder."<init>":()V
#26 = String #27 // Caught:
#27 = Utf8 Caught:
#28 = Methodref #23.#29 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#29 = NameAndType #30:#31 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#30 = Utf8 append
#31 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#32 = Methodref #21.#33 // java/lang/ArithmeticException.getMessage:()Ljava/lang/String;
#33 = NameAndType #34:#35 // getMessage:()Ljava/lang/String;
#34 = Utf8 getMessage
#35 = Utf8 ()Ljava/lang/String;
#36 = Methodref #23.#37 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#37 = NameAndType #38:#35 // toString:()Ljava/lang/String;
#38 = Utf8 toString
#39 = Class #40 // java/lang/Exception
#40 = Utf8 java/lang/Exception
#41 = String #42 // Caught general exception
#42 = Utf8 Caught general exception
#43 = Class #44 // java/lang/IllegalArgumentException
#44 = Utf8 java/lang/IllegalArgumentException
#45 = String #46 // Test exception
#46 = Utf8 Test exception
#47 = Methodref #43.#48 // java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
#48 = NameAndType #5:#20 // "<init>":(Ljava/lang/String;)V
#49 = Methodref #50.#51 // java/lang/String.length:()I
#50 = Class #52 // java/lang/String
#51 = NameAndType #53:#54 // length:()I
#52 = Utf8 java/lang/String
#53 = Utf8 length
#54 = Utf8 ()I
#55 = Class #56 // java/lang/NullPointerException
#56 = Utf8 java/lang/NullPointerException
#57 = String #58 // NullPointerException:
#58 = Utf8 NullPointerException:
#59 = Methodref #55.#33 // java/lang/NullPointerException.getMessage:()Ljava/lang/String;
#60 = String #61 // General exception:
#61 = Utf8 General exception:
#62 = Methodref #39.#33 // java/lang/Exception.getMessage:()Ljava/lang/String;
#63 = Class #64 // java/bytecode/ExceptionExample
#64 = Utf8 java/bytecode/ExceptionExample
#65 = Utf8 Code
#66 = Utf8 LineNumberTable
#67 = Utf8 exceptionExample
#68 = Utf8 StackMapTable
#69 = Class #70 // java/lang/Throwable
#70 = Utf8 java/lang/Throwable
#71 = Utf8 exceptionThrows
#72 = Utf8 Exceptions
#73 = Utf8 multipleCatchExample
#74 = Utf8 SourceFile
#75 = Utf8 ExceptionExample.java
{
public void exceptionExample();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: bipush 10
2: iconst_0
3: idiv
4: istore_1
5: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #13 // String Finally block
10: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: goto 87
16: astore_1
17: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
20: new #23 // class java/lang/StringBuilder
23: dup
24: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
27: ldc #26 // String Caught:
29: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: aload_1
33: invokevirtual #32 // Method java/lang/ArithmeticException.getMessage:()Ljava/lang/String;
36: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: invokevirtual #36 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
42: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
48: ldc #13 // String Finally block
50: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
53: goto 87
56: astore_1
57: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
60: ldc #41 // String Caught general exception
62: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
65: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
68: ldc #13 // String Finally block
70: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
73: goto 87
76: astore_2
77: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
80: ldc #13 // String Finally block
82: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
85: aload_2
86: athrow
87: return
Exception table:
from to target type
0 5 16 Class java/lang/ArithmeticException
0 5 56 Class java/lang/Exception
0 5 76 any
16 45 76 any
56 65 76 any
LineNumberTable:
line 11: 0
line 17: 5
line 18: 13
line 12: 16
line 13: 17
line 17: 45
line 18: 53
line 14: 56
line 15: 57
line 17: 65
line 18: 73
line 17: 76
line 18: 85
line 19: 87
public void exceptionThrows() throws java.lang.IllegalArgumentException;
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: new #43 // class java/lang/IllegalArgumentException
3: dup
4: ldc #45 // String Test exception
6: invokespecial #47 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
9: athrow
LineNumberTable:
line 23: 0
Exceptions:
throws java.lang.IllegalArgumentException
public void multipleCatchExample();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: aconst_null
1: astore_1
2: aload_1
3: invokevirtual #49 // Method java/lang/String.length:()I
6: istore_2
7: goto 71
10: astore_1
11: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
14: new #23 // class java/lang/StringBuilder
17: dup
18: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
21: ldc #57 // String NullPointerException:
23: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: aload_1
27: invokevirtual #59 // Method java/lang/NullPointerException.getMessage:()Ljava/lang/String;
30: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: invokevirtual #36 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
39: goto 71
42: astore_1
43: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
46: new #23 // class java/lang/StringBuilder
49: dup
50: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
53: ldc #60 // String General exception:
55: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
58: aload_1
59: invokevirtual #62 // Method java/lang/Exception.getMessage:()Ljava/lang/String;
62: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
65: invokevirtual #36 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
68: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
71: return
Exception table:
from to target type
0 7 10 Class java/lang/NullPointerException
0 7 42 Class java/lang/Exception
LineNumberTable:
line 29: 0
line 30: 2
line 35: 7
line 31: 10
line 32: 11
line 35: 39
line 33: 42
line 34: 43
line 36: 71
}
SourceFile: "ExceptionExample.java"
详细说明
异常处理表(Exception Table)
异常处理表定义了try-catch-finally的字节码范围和处理方式:
Exception table:
from to target type
0 5 16 Class java/lang/ArithmeticException
0 5 56 Class java/lang/Exception
0 5 76 any
16 45 76 any
56 65 76 any
- from - try块起始字节码偏移
- to - try块结束字节码偏移
- target - catch块起始字节码偏移
- type - 捕获的异常类型(
any表示finally块或任何异常)
异常相关指令
athrow- 抛出异常对象astore- 将异常对象存储到局部变量(catch块中)
finally块的实现
finally块会被复制到每个可能的退出路径:
1. try块正常结束
2. catch块处理完异常后
3. 异常未被捕获时
76: astore_2 // 存储异常到局部变量2
77: ... // finally块代码
85: aload_2 // 重新加载异常
86: athrow // 重新抛出异常
throws声明
方法声明中可能抛出的异常记录在Exceptions属性中:
Exceptions:
throws java.lang.IllegalArgumentException
多个catch块
多个catch块按照从具体到一般的顺序排列:
Exception table:
from to target type
0 7 10 Class java/lang/NullPointerException // 更具体的异常
0 7 42 Class java/lang/Exception // 更一般的异常
JVM会按照异常表的顺序匹配异常类型,找到第一个匹配的catch块。
异常处理流程
- 当异常发生时,JVM查找异常表
- 找到匹配的异常类型和字节码范围
- 跳转到对应的target位置(catch块)
- catch块处理完后,如果有finally块,执行finally块
- 如果finally块没有重新抛出异常,方法正常返回