Java - 插入参数(参数)时输出已更改

时间:2015-04-14 01:24:45

标签: java

对于以下代码,在插入参数" A2 a"和" B2 b"在A2类和B2类中,输出已经改变。我想知道为什么?

A1班级:

public class A2 
{
    public void m1(A2 a)
    {
        System.out.println("A2");
    }

}

B2班级:

public class B2 extends A2 
{
    public void m1(B2 b)
    {
        System.out.println("B2");
    }

}

主类(运行程序):

public class D2 
{
    public static void main(String[] args)
    {
        A2 x = new A2();
        A2 y = new B2();
        B2 z = new B2();

        y.m1();
    }

}

2 个答案:

答案 0 :(得分:1)

在插入参数之前,B2.m1()覆盖A2.m1(),因此调用的方法仅依赖于接收器的运行时类型。

插入参数后,两个m1方法具有不同的类型签名,B2.m1(B2 b)不再覆盖A2.m1(A2 a)。这样调用的方法取决于接收者和发送参数的编译时类型。

假设:

   A2 x = new A2();
   A2 y = new B2();
   B2 z = new B2();

   y.m1(x);  // calls A2.m1(A2 a)
   y.m1(z);  // calls A2.m1(A2 a)
   z.m1(x);  // calls A2.m1(A2 a)
   z.m1(z);  // calls B2.m1(B2 b)

在添加参数之前:

   y.m1();    // would have called B2.m1()

这是你看到的差异吗?

编辑:下面添加了很多详细信息。

首先,您可以将签名视为与数字结合的方法的名称 和形式参数的类型。您可能会看到它们以“m1:(LA2;)V”或“m1:(LB2;)V”的形式写入 (名为“m1”的方法,它接受类类型A2的1个参数并返回Void)。但我会在这里 写m1(A2)m1(B2)以使其更容易。

确定最终调用的方法基本上有两个步骤。编译时步骤 和一个运行时步骤。编译时步骤确定要调用的方法的签名。该 运行时步骤通过查找具有该签名的那个类来选择要调用的实际方法 对象的创建时间。如果它没有在那里找到它,它会向上移动类层次直到a 找到带有该签名的方法(必须有一个或者它不会被编译)。

要查找签名,使用编译时类型(也称为“静态类型”)(那些 用于声明变量)。这适用于接收器和参数。

那么让我们看看在每种情况下选择的签名:

y.m1(x)

y被声明为A2,因此唯一可能的签名是m1(A2)

y.m1(z)

同样,yA2,因此唯一的可能性是m1(A2)zB2的事实很好 您可以随时传递B2,预计会出现A2。 (在此过程中,事实是这样的 在运行时y将创建为B2无关!)

z.m1(x)

zB2。在B2中有两种可能性。 m1(B2)和继承的m1(A2)。自x以来 是A2,第一个是不可能的(您无法通过A2预期B2,所以一次 我们再次留下m1(A2)

z.m1(z)

zB2。在B2中有两种可能性。 m1(B2)和继承的m1(A2)。自z以来 是B2,两者都是可能的。在这里,Java有一个规则来选择哪个是带有的 最具体(最派生)的参数类型,因此选择m1(B2)

在上述所有情况中,没有什么可以在运行时决定,因为在每种情况下都有 只有一个具有给定签名的方法。以y.m1(z)为例。在运行时,Java会寻找 在编译时步骤中确定的带有签名m1(A2)的方法。

首先,它会在B2类中查找,因为y是作为B2对象创建的。但这里没有方法 有那个签名(只有m1(B2))。一旦z成为B2对象,这并不重要 签名是在编译时步骤中确定的,只会调用具有该签名的方法 在运行时。因此,Java上升到层次结构并在类中找到具有正确签名的方法 A2,因此结果就是你所看到的。 (请注意,这是概念性的和实际的 为了使它成为正确的方法而生成的代码比那更聪明。)

现在,在添加参数之前,两个m1方法都被命名为“m1”并且没有参数,因此它们具有 相同的签名m1()。所以看着

y.m1()

编译时步骤:yA2,因此唯一可能选择的是m1()。

然后在运行时,由于y被创建为B2,Java在类B2中开始寻找方法 有那个签名。它找到一个! m1()中的B2方法与m1()具有相同的签名 A2中的方法。换句话说,与上述情况不同,B2。m1真正覆盖A2.m1。所以java是 很高兴在m1()中调用B2的版本,正如您在添加参数之前所看到的那样。

这就是故事。我知道这很令人困惑,但它对理解Java(和其他对象)非常关键 你正在深入研究这种语言而且非常好。

阅读有关覆盖与重载,多态,动态调度和虚拟的所有内容 方法表。

我认为最重要的一点是:

- 只有覆盖方法时才会根据运行时进行动态调度    接收器的类型

- 派生类中的方法仅在签名时覆盖基类中的方法    是一样的

答案 1 :(得分:0)

y.m1()现在需要将参数传递给方法。这段代码甚至可以编译吗?由于您已将y构造为B2对象,因此调用y.m1()将运行B2对象中的代码而不是A2对象。

https://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html