【JVM学习笔记】-字节码-第二章 通过字节码分析this关键字和异常表的重要作用

我们使用以下简单的代码来学习本章知识

public class MyTest2 {

    public void test() {
        try {
            FileInputStream is = new FileInputStream("test.txt");
            ServerSocket socket = new ServerSocket(9999);
            socket.accept();
        }catch (FileNotFoundException ex){

        }catch (IOException ex){

        }catch (Exception ex) {

        }finally {
            System.out.println("finally");
        }
    }
}

通过javap -verbose看一下编译的结果, 也就是该java文件反应在字节码之后看看具体的内容

 public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=4, args_size=1
         0: new           #2                  // class java/io/FileInputStream
         3: dup
         4: ldc           #3                  // String test.txt
         6: invokespecial #4                  // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
         9: astore_1
        10: new           #5                  // class java/net/ServerSocket
        13: dup
        14: sipush        9999
        17: invokespecial #6                  // Method java/net/ServerSocket."<init>":(I)V
        20: astore_2
        21: aload_2
        22: invokevirtual #7                  // Method java/net/ServerSocket.accept:()Ljava/net/Socket;
        25: pop
        26: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        29: ldc           #9                  // String finally
        31: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        34: goto          84
        37: astore_1
        38: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        41: ldc           #9                  // String finally
        43: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        46: goto          84
        49: astore_1
        50: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        53: ldc           #9                  // String finally
        55: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        58: goto          84
        61: astore_1
        62: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        65: ldc           #9                  // String finally
        67: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        70: goto          84
        73: astore_3
        74: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        77: ldc           #9                  // String finally
        79: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        82: aload_3
        83: athrow
        84: return
      Exception table:
         from    to  target type
             0    26    37   Class java/io/FileNotFoundException
             0    26    49   Class java/io/IOException
             0    26    61   Class java/lang/Exception
             0    26    73   any
      LineNumberTable:
        line 17: 0
        line 19: 10
        line 20: 21
        line 28: 26
        line 29: 34
        line 21: 37
        line 28: 38
        line 29: 46
        line 23: 49
        line 28: 50
        line 29: 58
        line 25: 61
        line 28: 62
        line 29: 70
        line 28: 73
        line 29: 82
        line 30: 84
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           10      16     1    is   Ljava/io/FileInputStream;
           21       5     2 socket   Ljava/net/ServerSocket;
            0      85     0  this   Lcom/turnsole/myjvm/MyTest2;
      StackMapTable: number_of_entries = 5
        frame_type = 101 /* same_locals_1_stack_item */
          stack = [ class java/io/FileNotFoundException ]
        frame_type = 75 /* same_locals_1_stack_item */
          stack = [ class java/io/IOException ]
        frame_type = 75 /* same_locals_1_stack_item */
          stack = [ class java/lang/Exception ]
        frame_type = 75 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 10 /* same */

关于常量池之前的内容,以及构造方法,前面一章节的笔记中已经记载的很详细. 此处我们单独把我们写的test()方法拿出来看一下
在这里插入图片描述

1.1 this关键字

🤔思考
test()方法明明是没有形参的,为何我们的编译结果还是会有一个形参存在呢?

其实这个问题的答案在前面一章已经给出来了. 虽然我们的test()方法没有写任何形参,但是仍然会有一个 JVM创建的形参,并且会作为方法的第一个形参传入,它就是this

对于Java类中的每个实例方法(非static方法),其在编译后所生成的字节码当中,方法参数的数量总是会比源代码中方法参数的数量多一个(this), 它位于方法的第一个参数位置处; 这样我们就可以在Java的实例方法中使用this来访问当前对象的属性以及其他方法;

这个操作是在编译期间完成的,即由javac编译器在编译的时候将对this的访问转化为对一个普通实例方法参数的访问,接下来在运行期间,由 JVM 在调用实例方法时, 自动向实例方法传入该this参数. 所以,在实例方法的局部变量表中, 至少会有一个指向当前对象的局部变量;


locals为4, 阅读下我们开始写的代码,思考下分别指的是几个局部变量呢?

自问自答, 分别是 this,is,socket,以及Java在运行过程中可能会出现的一个异常

虽然我们上面写了三个catch代码块(创建了三个异常对象),但是Java语法规定,如果有异常,只能会进入这三个catch中的一个. 因此也就只会有一个异常对象创建出来

1.2 异常表 学习

在这里插入图片描述
exception_table, 这里存放的是处理异常的信息, 每个exception_table表项由start_pc,end_pc,handler_pc,catch_type组成

  • start_pcend_pc

表示在code数组中的从start_pcend_pc处(包含start_pc,不包含end_pc)的指令抛出的异常会由这个表项来处理

  • handler_pccatch_type

handler_pc表示处理异常的代码的开始处. catch_type表示会被处理的异常类型, 它指向常量池里的一个异常类. 当 catch_type为0时, 表示处理所有的异常.

比方说,从start_pcend_pc这段的代码在执行时出现了异常, 会去找到对应的catch_type,然后执行handler_pc起始的字节码(助记符中有一个 goto表示遇到异常,跳转到多少行代码进行执行)

借助 jclasslib工具,我们看下 JVM对异常的处理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里 start PC,end PC等对应的index值,是对应上上图中Bytecode红色数字

如第一个是指从 index 0index 26 之间的字节码在执行过程中如果出现了异常FileNotFoundException异常,那么执行 Handler PC37 助记符为astore_1意思是将捕获到的异常对象的引用赋值给ex局部变量,因为我们catch代码块中没有任何操作代码,这个时候👇
在这里插入图片描述
38,41,43就是finally代码块中具体的代码执行,首先获取Java.lang.System的静态变量public final static PrintStream out,然后助记符ldcPush item from run-time constant pool 将字符串 finally从运行时常量池中推送出来,然后助记符invokevirtual调用实例方法,println,最后助记符goto,跳转到第84个助记号在这里插入图片描述
很明显返回。无返回值。

注意
异常表中,我们发现除了我们代码中自己catch的三个异常之外,还有一个catch_type为any的异常,这是编译器为我们自动生成的异常。表示处理所有异常,即当catch_type为0时表示所有的异常

1.2.1 Java字节码对于异常的处理方式

  1. 统一采用异常表的方式来对异常进行处理.
  2. 在 jdk 1.4.2 之前的版本中,并不是使用异常表的方式来对异常进行处理的, 而是采用特定的指令方式;
  3. 当异常处理存在 finally语句块时, 现代化的JVM采用的处理方式是将finally语句块的字节码拼接到每一个catch块后面,换句话说,程序中存在多少个catch块,就会在每一个catch块后面重复多少个finally语句块的字节码;

1.2.2 抛出的异常和catch的异常在字节码上的区别

public void test() throws FileNotFoundException, IOException {
        try {
            FileInputStream is = new FileInputStream("test.txt");

            ServerSocket socket = new ServerSocket(9999);
            socket.accept();
        }catch (FileNotFoundException ex){

        }catch (IOException ex){

        }catch (Exception ex) {

        }finally {
            System.out.println("finally");
        }
    }

在这里插入图片描述

很明显能看到, throws出来的异常作为和Code平级存在
其实也不难理解,Code是方法体中的具体执行代码,catch也在方法体中, 而throws则是在方法上的定义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值