先上代码,先睹为快
public abstract class A {
public void print() {
System.out.println("A print ...");
}
public abstract void sayHello();
}
public class B extends A {
@Override
public void sayHello() {
System.out.println("hello ...");
}
}
public class AbstractTest {
public static void main(String[] args) throws IOException {
A obj = new B();
System.in.read();
System.out.println(obj);
}
}
上面的代码比较简单,直接跑起来
多态允许具体访问时实现方法的动态绑定。Java对于动态绑定的实现主要依赖于方法表。
本文我们从字节码角度看方法表,来看看Java底层怎么实现多态的
jps 看看刚才跑的java进程
~ » jps xinye@wangmingxin03
5380
82150 LoopTest
13785 Launcher
14043 Jps
13789 AbstractTest
13775 KotlinCompileDaemon
13789
然后介绍一个Java 内置的工具
HSDB 全称是:Hotspot Debugger,是内置的 JVM 工具,可以用来深入分析 JVM 运行时的内部状态。
HSDB 位于 JDK 安装目录下的 lib/sa-jdi.jar 中, 启动 HSDB
/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib
sudo java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
找到我们B这个类
public class pers.wmx.springbootfreemarkerdemo.bytecode.B @0x00000007c0060c18
B的内存指针地址 0x00000007c0060c18
通过 Tools->Inspector 输入上面找到的地址找到 vtable, 就是方法表
可以看到它的 vtable 的长度为 7。先说结论:有 5 个是 上帝类 Object 的 5 个方法,一个是 B 覆写的 sayHello 方法,一个是继承 A 的 printMe 方法
vtable 分配在 instanceKlass 对象实例的内存末尾,instanceKlass大小在 64 位系统的大小为 0x1b8
注:0x开头的都是16进制
8 * 16^0 + 11 * 16^1 + 1 * 16^2 = 440
0x00000007c0060c18 + 0x1b8 = 0x7c0060dd0
定位到方法表
mem 0x7c0060dd0 7
可以查看 vtable 内存起始地址的 7 个方法指针地址了
vtable 里存储的是指向方法内存的指针
前5个是继承于Object的方法,直接指向Object的方法
再看剩下2指针
我们看下B的vtable图示:
vtable 是 Java 实现多态的基石,如果一个方法被继承和重写,会把 vtable 中指向父类的方法指针指向子类自己的实现。
- Java 子类会继承父类的 vtable。Java 所有的类都会继承 java.lang.Object 类,Object 类有 5 个虚方法可以被继承和重写。当一个类不包含任何方法时,vtable 的长度也最小为 5,表示 Object 类的 5 个虚方法
- final 和 static 修饰的方法不会被放到 vtable 方法表里
- 当子类重写了父类方法,子类 vtable 原本指向父类的方法指针会被替换为子类的方法指针
- 子类的 vtable 保持了父类的 vtable 的顺序
说点什么
您将是第一位评论人!