奥秘应该在方法表了吧,因为这是运行时的行为,所以从编译结果也看不出什么来,所以要分析方法表了,不过初学java,还不太懂. 例子: public class Test { public static void main(String[] args) { Base base = new Base(); base.print(); Base sub = new Sub(); sub.print(); } }class Base { public void print() { System.out.println("Base::print"); } }class Sub extends Base { public void print() { System.out.println("Sub::print"); } }编译后的Test.classCompiled from "Test.java" public class Test extends java.lang.Object{ public Test(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: new #2; //class Base 3: dup 4: invokespecial #3; //Method Base."<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4; //Method Base.print:()V 12: new #5; //class Sub 15: dup 16: invokespecial #6; //Method Sub."<init>":()V 19: astore_2 20: aload_2 21: invokevirtual #4; //Method Base.print:()V 24: return} 编译器只知道根据类型确定调用的是Method Base.print:()V 具体在方法表里怎么去匹配的就自己研究一下吧.
java默认所有的方法都是virtual的(final,private,static应该去掉了),内部如何支持的没仔细分析过,应该也是类似虚表的机制吧.
它们概念有相识的 地方 ,但还是有区别的
例子:
public class Test { public static void main(String[] args) {
Base base = new Base();
base.print();
Base sub = new Sub();
sub.print();
}
}class Base { public void print() {
System.out.println("Base::print");
}
}class Sub extends Base { public void print() {
System.out.println("Sub::print");
}
}编译后的Test.classCompiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnpublic static void main(java.lang.String[]);
Code:
0: new #2; //class Base
3: dup
4: invokespecial #3; //Method Base."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4; //Method Base.print:()V
12: new #5; //class Sub
15: dup
16: invokespecial #6; //Method Sub."<init>":()V
19: astore_2
20: aload_2
21: invokevirtual #4; //Method Base.print:()V
24: return}
编译器只知道根据类型确定调用的是Method Base.print:()V
具体在方法表里怎么去匹配的就自己研究一下吧.
不过那个虚函数表又是什么东西?为什么会跟我的问题有关
谁能告诉我哪种类型的书上能找到这个概念?最好就直接告诉我书的名字
不过重写的方法和重载的方法都会用到这个指令.
而虚函数不同,含有虚函数的每个类的对象,都要维护一个虚函数表,其指针其实就是该类的第一个成员(当然,对程序员来说是不可见的),运行时,首先通过类的实例得到虚函数表,然后从表中查到相应的函数入口,实现所谓的运行时“迟绑定”。所以,即使是用一个基类的指针,但如果它实际指向的是一个派生类对象,那么运行时,得到的虚函数表将是此派生类的虚函数表,最终调用的也将是派生类的函数。
当然,虚函数为了实现这种灵活的“迟绑定”,也必须付出代价,那就是每个对象都必须维护一张虚函数表,当虚函数很多时,这个表是很巨大的。
呵呵
有点好奇,怎么才能像你这样看到编译后的东西,是用什么打开的.class文件?
就可以看对应的虚拟机指令代码了.
简单的问题看指令就明白了,复杂的还要看里面的方法表,类型信息等.