为什么我可以从超类访问重写的子类方法?

Why can I access overridden subclass methods from the superclass?

为什么method()调用覆盖的子类method2而不是BaseClass中的method2

public class BaseClass {
    public void method(){
        System.out.println("method() called");
        method2();
    }
    public void method2(){
        System.out.println("method2() called");
    }
}

public class ChildClass extends BaseClass {
    public void method2(){
        System.out.println("method2() from BaseClass");
    }
}


public class Main {

    public static void main(String[] args) {
        ChildClass obj = new ChildClass();
        obj.method();
    }
}

这就是运行时多态(Dynamic Method Dispatch)的概念。因为你正在将 ChildClass 的对象(实例)分配给 obj 引用变量,它会调用子 class.

method

总是首先调用创建实例的 class 的方法。如果该方法不存在于该特定子项 class,则将调用父项的继承方法。

如果你来自C++角:

  • Java 中的所有实例方法(非静态)都是 virtual
  • 所有 class 方法(静态)都不是。

这就是你的情况发生的原因。

这也是为什么 Java 编译器会抱怨(警告)如果你通过对象访问静态方法,你应该通过不同的 class 调用,因为调用“对象的静态方法”可能不明确,因为它可能是两个具有相同签名的静态方法被调用。

扩展您的示例:

package Whosebug.staticcalls;

public class BaseClass {

    public void method() {
        System.out.println("method() called");
        method2();
    }
    public void method2() {
        System.out.println("method2() called");
    }



    static public void sayHello() {
        System.out.println("BaseClass.sayHello()");
    }

}

package Whosebug.staticcalls;

public class ChildClass extends BaseClass {

    public void method2() { // compiler warning: The method method2() of type ChildClass should be tagged with @Override since it actually overrides a superclass method
        System.out.println("method2() from BaseClass");
    }

    public void originalCallToBaseMethod2() {
        super.method2(); // will run BaseClass.method2()
    }



    static public void sayHello() {
        System.out.println("ChildClass.sayHello()");
    }

}

package Whosebug.staticcalls;

public class Main {

    public static void main(final String[] args) {
        final ChildClass obj = new ChildClass();
        System.out.println("\nCalling obj.method(); ...");
        obj.method();

        System.out.println("\nCalling obj.sayHello(); ...");
        obj.sayHello(); // compiler warning: The static method sayHello() from the type ChildClass should be accessed in a static way

        System.out.println("\nCalling ChildClass.sayHello(); ...");
        ChildClass.sayHello(); // the proper call

        System.out.println("\nCalling BaseClass.sayHello(); ...");
        BaseClass.sayHello(); // but you can also explpicitly call the other method

        System.out.println("\nCalling obj.originalCallToBaseMethod2(); ...");
        obj.originalCallToBaseMethod2(); //
    }

}

这里你可以看到我说的例子。

注意:在Main.main()的最后一次调用中,我们仍然可以调用BaseClass.method2(),但不能直接调用。我们必须在 ChildClass 中才能做到这一点,这是通过 super keyword/reference.

完成的

一点题外话,以完成寻址模式: 如果你在内部 class 中并且需要调用外部 class 中的遮蔽名称,你可以使用 Outer.this.method():

package Whosebug.staticcalls;

import Whosebug.staticcalls.OuterInner.Outer.Inner;

public class OuterInner {

    class Outer {

        void method() {
            System.out.println("OuterInner.Outer.method()");
        }

        class Inner {

            void method() {
                System.out.println("OuterInner.Outer.Inner.method()");
            }

            void callOuter() {
                Outer.this.method();
            }
        }
    }

    public static void main(final String[] args) {
        final OuterInner oi = new OuterInner();
        final Outer outer = oi.new Outer();
        final Inner inner = outer.new Inner();

        System.out.println("\nCalling inner.method(); ...");
        inner.method();

        System.out.println("\nCalling inner.callOuter(); ...");
        inner.callOuter();
    }

}