Java 从超类构造函数调用基类重写方法

Java 从超类构造函数调用基类重写方法,java,inheritance,Java,Inheritance,考虑到下面的代码块,我想了解调用print in子类的原因和方式,重点是如何调用print in子类: class Super { Super() { // what happens so that Sub's method print() is invoked print(); } public void print() { System.out.println("in super"); } } clas

考虑到下面的代码块,我想了解调用print in子类的原因和方式,重点是如何调用print in子类:

class Super {

    Super() {

        // what happens so that Sub's method print() is invoked
        print();
    }

    public void print() {

        System.out.println("in super");
    }
}

class Sub extends Super {

    Sub() {

        super();
    }

    public void print() {

        System.out.println("in sub");
    }
}

public class TestClass {

    public static void main(String[] args) {


        Super s = new Sub(); // "in sub".. not so much expected

        s.print(); // "in sub".. as expected
    }
}
我的理解是,在编译时,类将获得与“属于”类的方法相关联的V-table指针
vtblPtr
。 因此,类
Super
应该引用它自己的方法
print()
的实现


为什么在
Super
的构造函数中调用
Sub
中的方法
print()
?这里到底发生了什么?

这里是JVM的
Super
类的外观(实际上,这是通过
javap-c Super
获得的人类可读版本)

class超级{
超级();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:aload_0
5:invokevirtual#2//方法打印:()V
8:返回
公开作废打印();
代码:
0:getstatic#3//Field java/lang/System.out:Ljava/io/PrintStream;
3:ldc#4//超级中的字符串
5:invokevirtual#5//方法java/io/PrintStream.println:(Ljava/lang/String;)V
8:返回
}
如您所见,
Super
内部的
print
构造函数在编译时未解析为
Super::print
。虚拟调用意味着它在运行时被解析,与
this
的类有关

我的理解是,在编译时,类将获得与“属于”类的方法相关联的V-table PointerVTBlptra。因此,类Super应该引用它自己的方法print()的实现

您对虚拟方法和重写有一个基本的误解。在Java中,每个非
静态
、非
私有
方法都是虚拟的。从任何地方对虚拟方法的每个虚拟(正常)调用都将调用与调用该方法的对象的类相关联的版本。该版本可能被继承,也可能不被继承,也可能不重写超类的方法


上隐式或显式地执行虚拟方法调用不会改变这一切。特别是,被初始化对象的实际类对每个构造函数都是可见的,并为所有方法调用提供上下文。事实上,这就是为什么构造函数调用自己的类提供的虚拟方法很少是个好主意。超类构造函数在子类构造函数之前运行,因此如果超类构造函数调用的方法恰好被子类重写,然后,该方法将在对象完全按照方法假设的方式运行之前运行。

这可能是有用的资源添加:我认为您混淆了C++和java:java中没有VTBLPTR的概念。我觉得java使用虚拟表来实现多态性?这是一个不存在的概念。每个JVM都可以自由地提出自己的方法。确实如此。HotSpot编译器将检测是否加载了任何子类,或者是否使用子类调用代码,然后决定是否执行虚拟分派、直接分派,甚至是代码的内联。从Java代码或字节码无法判断在某个时间点它是如何实际完成的。虽然我很确定我在某个地方读到过java中的每个类都与vtable相关联。谢谢你的澄清。
class Super {
  Super();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: invokevirtual #2                  // Method print:()V
       8: return        

  public void print();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String in super
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return        
}