字节码知识点整理
对于Java类中的每个实例方法(非static方法),其在编译后所生成的字节码当中,方法参数的数量总是会比源代码中方法参数的数量多一个(this),它位于方法的第一个参数位置处;这样,我们就可以在Java的实例方法中使用this来去访问当前对象属性以及其他方法。
这个操作是在编译期间完成的,即由javac编译器在编译的时候将对this的访问转化为对一个普通实例方法参数的访问,接下来在运行期间,由JVM在调用实例方法时,自动向实例方法传入该this参数。所以,在实例方法的局部变量表中,至少会有一个指向当前对象的局部变量。
Java字节码对于异常的处理方式:
1.统一采用异常表的方式来对异常进行处理。
2.在jdk1.4.2之前的版本中,并不是使用异常表的方式来对异常进行处理的,而是采用特定的指令方式。
3.当异常处理存在finally语句块时,现代化的JVM采取的处理方式是将finally语句块的字节码拼接到每一个catch块后面,换句话说,程序中存在多少个catch块,就会在每一个catch块后面重复多少个finally语句块的字节码。
栈帧(stack frame)
栈帧是一种用于帮助虚拟机执行方法调用与方法执行的数据结构。
栈帧本身是一种数据结构,封装了方法的局部变量表、动态链接信息,方法的返回地址以及操作数栈等信息。
符号引用,直接引用
有些符号引用是在类加载阶段或是第一次使用时就会转换为直接引用,这种转换叫做静态解析;另外一些符号引用则是在每次运行期转换为直接引用,这种转换叫做动态链接,这体现为Java的多态性。
invokeinterface
:调用接口中的方法,实际上是在运行期决定的,决定到底调用实现该接口的哪个对象的特定方法。
invokestatic
:调用静态方法。
invokespecial
:调用自己的私有方法、构造方法(<init>
)以及父类的方法。
invokevirtual
:调用虚方法,运行期动态查找的过程。
invokedynamic
:动态调用方法。
静态解析的4种情形:
1.静态方法
2.父类方法
3.构造方法
4.私有方法
以上4类方法称作非虚方法,他们是在类加载阶段就可以将符号引用转换为直接引用。
方法的静态分派:
Grandpa g1 = new Grandpa();
以上代码,g1的静态类型是Grandpa,而g1的实际类型(真正指向的类型)是Father。
我们可以得出这样一个结论:变量的静态类型是不会发生变化的,而变量的实际类型则是可以发生变化的(多态的一种体现),实际类型是在运行期方可确定。
示例代码:
public class MyTest5 {
//方法重载,是一种静态的行为,编译器就可以完全确定。
public void test(Grandpa grandpa) {
System.out.println("grandpa");
}
public void test(Father father) {
System.out.println("father");
}
public void test(Son son) {
System.out.println("son");
}
public static void main(String[] args) {
Grandpa g1 = new Father();
Grandpa g2 = new Son();
MyTest5 myTest5 = new MyTest5();
myTest5.test(g1);
myTest5.test(g2);
}
}
class Grandpa {
}
class Father extends Grandpa {
}
class Son extends Father {
}
方法的动态分派
方法的动态分派涉及到一个重要概念:方法接受者。
invokevirtual字节码指令单多态查找流程
比较方法重载(overload)与方法重写(overwrite),我们可以得到这样的结论:
方法重载是静态的,是编译期行为;方法重写是动态的,事运行期行为。
示例代码
public class MyTest6 {
public static void main(String[] args) {
Fruit apple = new Apple();
Fruit orange = new Orange();
apple.test();
orange.test();
apple = new Orange();
apple.test();
}
}
class Fruit{
public void test(){
System.out.println("Fruit");
}
}
class Apple extends Fruit{
@Override
public void test() {
System.out.println("Apple");
}
}
class Orange extends Fruit{
@Override
public void test() {
System.out.println("Orange");
}
}
针对于方法调用动态分派的过程,虚拟机会在类的方法区建立一个虚方法表的数据结构(virtual method table, vtable)
针对于invokeinterface指令来说,虚拟机会建立一个叫做接口方法表的数据结构(interface method table,itable)
示例代码:
public class MyTest7 {
public static void main(String[] args) {
Animal animal = new Animal();
Animal dog = new Dog();
animal.test("hello");
dog.test(new Date());
}
}
class Animal {
public void test(String str) {
System.out.println("animal str");
}
public void test(Date date) {
System.out.println("animal date");
}
}
class Dog extends Animal {
@Override
public void test(String str) {
System.out.println("dog str");
}
@Override
public void test(Date date) {
System.out.println("dog date");
}
}