对于这个代码:
public class Demo
{
public static void main(String[] args)
{
System.out.println( new B().m1() );
}
}
class A
{
int m1() {return m3();}
int m2() {return this.m3();}
int m3() {return 1;}
}
class B extends A
{
int m1() {return super.m1();}
int m3() {return 4;}
int m4() {return m2();}
}
返回4,但我不确定为什么。所以我是这样看的:new B().m1()创建一个B类型的对象并调用B类的m1()方法,this调用A类的super.m1(),它返回m3(),即1。我知道你可以在继承中重写方法,但m1()在类A中显式返回m3(),而不是在类B中,那么为什么返回4和输出而不是1?
Java的实例方法是严格的动态调度。
任何方法调用总是将根据this
引用的实际类型将击中该方法的"最具体"版本。
唯一的例外是super.x()
,它将调用您的super,但它是一次性的转移。在该方法中执行的任何调用都像往常一样执行动态分派。
:
new B().m1()
自然调用B的m1()
方法:因为实际实例的真实类型自然是B
。
该方法将调用A的m1()
。A1的m1()
代码为:
return m3();
当然是
的缩写return this.m3();
和this
在这里是仍然是B
的一个实例。因此,应用了动态调度,将调用class B
的m3()
impl,因此返回4
。
没有办法避免这种情况,除非重写所有这些方法。
需要考虑的2个重要事项:
static
方法不做这些事情。- 在java中,方法的名称包括参数类型(没有泛型,即擦除)和返回类型。所以,如果你有:
class Parent {
void foo(Object o) {}
}
class Child extends Parent {
void foo(String o) {}
}
...
Parent p = new Child();
p.foo("hello!");
将仍然调用父foo
,而不是子foo
,因为它们是不同的方法。这就是为什么你总是使用@Override
当你打算覆盖;这意味着,由于类型不匹配,当您实际上没有覆盖时,您会得到编译器错误。