目录

  • 1 javap工具
  • 2 运行流程
  • 3 分析i++与++i
  • 4 构造方法原理
  • 4.1 cinit
  • 4.2 init
  • 5 方法调用
  • 6 多态的原理
  • 7 异常处理


1 javap工具

Oracle提供了javap工具来反编译class文件:
javap -v Main.class 会显示反编译之后的class文件,主要是常量池和方法区的理解;

2 运行流程

java 字节码增强工具 java字节码教程_java

  • 首先把class的常量池信息,载入到JVM内存的方法区的运行时常量池
  • class方法的字节码,会存放到方法区
  • 启动主线程,分配栈内存,压入main()方法栈帧到栈,栈帧又有局部变量表(槽位)和最大操作数栈(已经在编译时确定了是已知的),程序计数器指向方法区的main的第一条语句由执行引擎执行;
  • 执行完毕之后,弹出栈帧,结束;

3 分析i++与++i

  • i++是先iload再执行iinc;
  • ++i是先iinc再iload;

iinc直接在局部变量表槽位上自加,而iload是会加载到操作数栈空间进行运算;
例如:a = a++
先把a的值取到操作数栈,在自己的局部变量表槽上执行自加操作,然后赋值操作是把操作数栈的值又存回局部变量表槽,所以a的值不变

4 构造方法原理

4.1 cinit

整个类初始化方法,编译器会按从上至下的顺序(先赋值后申明也可以),收集所有的静态代码块和静态成员变量赋值的代码,合并为一个特殊的方法<cinit>()v;

4.2 init

对象初始化方法,编译器会按照从上到下的顺序(先赋值后申明也可以),收集所有的{}代码块和成员变量赋值的代码,形成新的构造方法,但是原始的构造方法代码总是在最后;

5 方法调用

  • 调用构造方法指令:invokespecial
  • 调用私有方法指令:invokespecial
  • 调用final方法指令:invokespecial
  • 普通public方法指令:invokevirtual
  • 类调用静态方法指令:invokestatic
  • 对象调用静态方法指令:invokestatic

其中invokespecialinvokestatic在编译期间就能确定调用是哪个方法,但是invokevirtual不能确定(可能子类的可能父类的,多态);

6 多态的原理

当执行invokevirtual指令时:

  • 先通过栈帧中的对象引用找到对象;
  • 分析对象头,找到对象的实际class(因为可以用父类引用引用子类对象);
  • class结构中有vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了;
  • 查表得到方法的具体地址;
  • 执行方法的字节码;

7 异常处理

  • 单catch块:会在编译时生成一个Exception Table,监测片段代码,要是发生异常,就匹配异常类型,匹配成功,执行异常处理代码;
  • 多catch块:Exception Table存在多条异常条,每条都监控,槽位复用,同一时刻只有一个错误发生;
  • finally块:在字节码层面会把finally代码块放到try后面和每一个catch后面,都会存在;除此之外,编译器会自己加上任意类型的error和Exception->any用来捕捉try块和catch块中还会出现的异常和错误,一式三份
  • SOS!!!finally块里面不要写return语句,会吞掉异常;
  • return之前的值要暂存,当执行完善后工作后,才会真正return出去;