字节码分析-异常处理

源码

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

异常相关指令

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块。

异常处理流程

  1. 当异常发生时,JVM查找异常表
  2. 找到匹配的异常类型和字节码范围
  3. 跳转到对应的target位置(catch块)
  4. catch块处理完后,如果有finally块,执行finally块
  5. 如果finally块没有重新抛出异常,方法正常返回