实例字段的继承如何在这个特定的代码中工作?

use*_*652 44 java inheritance

class A
{
    int a = 2, b = 3;
    public void display()
    {
        int c = a + b;
        System.out.println(c);
    }
}
class B extends A
{
    int a = 5, b = 6;
}
class Tester
{
    public static void main(String arr[])
    {
        A x = new A();
        B y = new B();
        x.display();
        y.display();
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么输出为5,5?而不是5,11?.该y.display()方法如何工作?

Jon*_*eet 109

为什么输出为5,5?

因为A.display()只知道领域A.aA.b.这些是任何代码所A知道的唯一字段.看起来您希望声明B"覆盖"现有的字段声明.他们没有.他们宣布的字段隐藏的现有字段.变量实际上不像方法那样表现 - 覆盖变量的概念根本就不存在.从JLS第8.3节:

如果类声明了具有特定名称的字段,那么该字段的声明将被称为隐藏超类中具有相同名称的字段的任何和所有可访问声明,以及该类的超接口.

您可以通过更改来获得所需的效果,B以便其构造函数更改其继承的现有字段的值A:

class B extends A {
    B() {
        a = 5;
        b = 6;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这些不是变量声明.他们只是作业.当然在大多数代码中(好吧,我见过的大多数代码)中的字段A都是私有的,因此无法访问B,但这仅仅是为了解释语言行为的示例.


fab*_*ian 16

在课堂上A你宣布领域ab.该方法display使用这些字段.在课堂上,B您声明了同名的新字段.你实际上是在隐藏旧字段而不是"覆盖"它们.要为相同的字段分配不同的值,请使用构造函数:

class A {
    A(int a, int b) {
        this.a = a;
        this.b = b;
    }

    A() {
        this(2, 3);
    }

    int a,b;

    public void display() {
        int c=a+b;
        System.out.println(c);
    }
}

class B extends A {
    B() {
        super(5, 6);
    }
}
Run Code Online (Sandbox Code Playgroud)


And*_*ndy 13

这样做时:

class B extends A
{
    int a = 5, b = 6;
}
Run Code Online (Sandbox Code Playgroud)

你是不是重新定义 ab,你创建具有相同名称的新变量.所以,你最终用四个变量(A.a,A.b,B.a,B.b).

当你调用display()和计算的值c,A.aA.b会使用,不B.aB.b


Ami*_*rma 9

没有任何称为变量覆盖的东西.这就是为什么你在两种情况下得到相同的结果.


小智 6

原因是Java使用词法范围的概念来实现可变分辨率.

从根本上说,有两种方法可以解决函数中的自由变量('free'表示不是局部的,不是绑定到函数参数):

1)针对声明函数的环境

2)针对执行函数的环境(调用)

Java是第一种方式,因此方法中的自由变量[ 静态地,在编译期间 ]针对它们的词法范围(环境)进行解析,其中包括:

  • 方法参数和局部方法变量
  • 包含方法声明的类中的字段声明
  • 父类中的公共字段声明
  • 等等,继承链

您会在大多数编程语言中看到这种行为,因为它对开发人员是透明的,有助于防止变量阴影出错.

这与方法在Java中的工作方式相反:

class A {
    public void foo() {
        boo();
    }
    public void boo() {
        System.out.println("A");
    }
}
class B extends A {
    @Override
    public void boo() {
        System.out.println("B");
    }
}
class Main {
    public static void main(String[] args) {
        B b = new B();
        b.foo(); // outputs "B"
    }
}
Run Code Online (Sandbox Code Playgroud)

这称为动态调度:方法调用在运行时针对调用它的实际对象动态解析.


Pul*_*dha 5

编译代码时,它几乎变成了:

class A extends java.lang.Object
{
    int a=2,b=3;
    public void display()
    {
        int c=a+b;
        System.out.println(c);
    }
}
class B extends A
{
    int a = 5, b = 6;
    public void display()
    {
      super(); //When you call y.display() then this statement executes.
    }
}
class Tester
{
    public static void main(String arr[])
    {
        A x = new A();
        B y = new B();
        x.display();
        y.display();
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,当超级调用时,class A正在调用方法.

现在去方法吧class A.这int c = a + b;意味着 c = this.a + this.b;哪个是2 + 3.

结果是5.