为什么标有 //1 的行打印 57 而不是 39?

Why does the line marked with //1 print 57 instead of 39?

class X {
    protected int v = 0;

    public X() {
        v += 10;
    }

    public void proc(X p) {
        System.out.println(43);
    }
}

class Y extends X {
    public Y() {
        v += 5;
    }

    public void proc(X p) {
        System.out.println(57);
    }

    public int getV() {
        return v;
    }
}

class Z extends Y {
    public Z() {
        v += 9;
    }

    public void proc(Z p) {
        System.out.println(39);
    }
}

class Main {
    public static void main(String[] args) {
        X x = new Z();
        Y y = new Z();
        Z z = new Z();
        x.proc(z);// 1
        System.out.println(y.getV());
    }
}

据我所知,方法 proc() 是在 X 类型的对象上调用的,而 "holds" 是 Z 类型,并且在运行时 JVM 检查对象的类型并使用 proc() 覆盖方法method from Y.But 方法参数是Z类型,为什么不调用Z class的重载方法?

因为您要将 X 传递给 proc,所以 class Yproc 优先。

如果您传递一个声明为 Z 的实际 Z,它会打印 39.

Z x = new Z();
X y = new Z();
Z z = new Z();


z.proc(x); // prints 39
z.proc(y); // prints 57

发生这种情况是因为您没有覆盖 Z class 中的方法 'proc'。重写方法时,不能使用参数,它有原始参数 class 的子 class。如果您在 Z.proc(Z p) 上添加 @Override,您的代码将不会被编译。

让我们想象一下这是可能的,然后你可以在执行过程中使用 Z class 中的一些方法 Z.proc(Z p).

class Z extends Y {
    public Z() {
        v += 9;
    }

    public void proc(Z p) {
        someActions();
        System.out.println(39);
    }

    private void someActions() {
        System.out.println("Some actions");
    }

}

现在当你执行

X x = new Z();
x.proc(new X());

会发生什么? Xclass中没有'someActions'方法。它应该如何运作?这就是 Z.proc(Z p) 不覆盖 X.proc(X p) 的原因。 Class Z,有两种不同的方法:Z.proc(Z p) 和 Y.proc(X p)。

当你打电话给

X x = new Z();
x.proc(new Z());

JVM 寻找最接近 Z 'proc(X)' 到 Z class 的重写或原始方法(因为 X class 有 'proc(X)' 方法)在 Y class 并执行 Y.proc(x p)。这就是您在输出中看到“57”的原因。

只是为了详细说明 Federico 的回答。 "x" 对象实际上是一个指向内存中 "Z" 实例的 "X" 变量。 "Z"实例只有两个方法:

void proc(Z p)
void proc(X p)

但是运行时只知道在 "X" class 中实现的第二个方法,它被 "Y" class 中实现的方法覆盖。 所以,当你打电话时:

x.proc(z)

运行时只知道第二个方法并调用它,执行被覆盖的方法。