/** * Created By poplar on 2019/11/9 * 源码: */ publicclassByteCodeTest1{ privateint a = 1;
publicintgetA(){ return a; }
publicvoidsetA(int a){ this.a = a; } }
执行javap命令后的字节码文件
Compiled from "ByteCodeTest1.java" public class com.poplar.bytecode.ByteCodeTest1 { public com.poplar.bytecode.ByteCodeTest1(); public int getA(); public void setA(int); }
执行javap -c命令后的字节码文件
Compiled from "ByteCodeTest1.java" publicclasscom.poplar.bytecode.ByteCodeTest1{ public com.poplar.bytecode.ByteCodeTest1(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field a:I 9: return
/** * Created By poplar on 2019/11/9 * 从字节码分析得出的结论: * 成员变量的初始化是在构造方法中完成的,有多少个构造方法,初始化指令就会调用几次 * 静态成员变量同样是在clinit方法完成的,不管有多少个静态变量都是在该方法完成初始化 */ publicclassByteCodeTest2{
publicstaticvoidmain(String[] args){ ByteCodeTest4 byteCodeTest4 = new ByteCodeTest4(); //方法重载,是一种静态的行为,编译期就可以完全确定 Grandpa g1 = new Father(); Grandpa g2 = new Son(); byteCodeTest4.test(g1);//Grandpa byteCodeTest4.test(g2);//Grandpa } }
classGrandpa{
}
classFatherextendsGrandpa{
}
classSonextendsFather{
}
测试6:
package com.poplar.bytecode;
/** * Created By poplar on 2019/11/10 * 方法的动态分派 * 方法的动态分派涉及到一个重要概念:方法接收者。 * invokevirtua1字节码指令的多态查找流程 * 比较方法重载(overload)与方法重写(overwrite) ,我们可以得到这样一个结论: * 方法重载是静态的,是编译期行为; * 方法重写是动态的,是运行期行为。 */ publicclassByteCodeTest5{ publicstaticvoidmain(String[] args){ Fruit apple = new Apple(); apple.test();//<com/poplar/bytecode/Fruit.test>将符号引用转换为直接引用
/** * Created BY poplar ON 2019/12/4 * 基于栈的解释器的执行过程概念模型 */ publicclassBasicStackExecutionProcess{
publicintcalc(){ int a = 100; int b = 200; int c = 300; return (a + b) * c;
/* public int calc(); descriptor: ()I flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=1 0: bipush 100 执行地址偏移量为0 将100推送至栈顶 2: istore_1 执行地址偏移量为2 将栈顶的100出栈并存放到第一个局部变量Slot中 3: sipush 200 6: istore_2 7: sipush 300 10: istore_3 11: iload_1 执行地址偏移量为11 将局部变量中第一个Slot中的整型值复制到栈顶 12: iload_2 13: iadd 将栈顶的两个元素出栈并作整形加法,然后把结果重新入栈 14: iload_3 15: imul 将栈顶的两个元素出栈并作整形乘法,然后把结果重新入栈 16: ireturn 结束方法并将栈顶的值返回给方法调用者 LineNumberTable: line 10: 0 line 11: 3 line 12: 7 line 13: 11 LocalVariableTable: Start Length Slot Name Signature 0 17 0 this Lcom/poplar/bytecode/BasicStackExecutionProcess; 3 14 1 a I 7 10 2 b I 11 6 3 c I */ }
publicstaticvoidmain(String[] args){ BasicStackExecutionProcess process = new BasicStackExecutionProcess(); int res = process.calc(); System.out.println(res); } }
动态分派:
package com.poplar.bytecode;
/** * Created BY poplar ON 2019/12/4 * 动态分派的演示与证明: * 在动态分派中虚拟机是如何知道要调用那个方法的? */ publicclassDynamicDispatch{
publicstaticvoidmain(String[] args){ StaticDispatch dispatch = new StaticDispatch(); /*Human man = new Man(); Human woMan = new Woman(); dispatch.hello(man); dispatch.hello(woMan);*/
Human human = new Woman(); human = new Man(); dispatch.hello((Woman) human); dispatch.hello((Man) human); //java.lang.ClassCastException: main.java.com.poplar.bytecode.WoMan cannot be cast to main.java.com.poplar.bytecode.Man } }
现代JVM在执行Java代码的时候,通常都会将解释执行与编译执行二者结合起来进行.
所谓解释执行,就是通过解释器来读取字节码,遇到相应的指令就去执行该指令
所谓编译执行,就是通过即时编译器(Just in Time, JIT)将字节码转换为本地机器码来执行;现代JoM会根据代码热点来生成目应的本地机器码 在布尔德E马文项目