实例字段的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(); } }
为什么输出为5,5? 而不是5,11? y.display()
方法将如何工作?
为什么输出是5,5?
因为A.display()
只知道字段Aa
和Ab
。 这些是A
中的任何代码知道的唯一的字段。 它看起来像你期望B
的声明“覆盖”现有的字段声明。 他们没有。 他们宣布隐藏现有领域的新领域。 variables的行为并不像方法那样 – 重写variables的概念根本就不存在。 从JLS部分8.3 :
如果这个类声明了一个具有特定名字的字段,那么这个字段的声明被说成隐藏了超类中相同名字的字段以及类的超接口中任何和所有可访问的声明。
您可以通过更改B
来获得所需的效果,以便其构造函数更改从A
inheritance的现有字段的值:
class B extends A { B() { a = 5; b = 6; } }
请注意,这些不是variables声明。 他们只是任务。 当然,在大多数代码中(大多数代码是我见过的), A
的字段是私有的,所以不能从B
访问,但这仅仅是为了解释语言行为。
在A
类中A
你声明域a
和b
。 方法display
使用这些字段。 在B
class中,您声明了同名的NEW字段。 你实际上隐藏的旧领域不“覆盖”他们。 要将不同的值分配给相同的字段,请使用构造函数:
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); } }
当这样做时:
class B extends A { int a = 5, b = 6; }
你没有重新定义 a
和b
,你正在创build具有相同名称的新variables。 所以你最终得到四个variables( Aa
, Ab
, Ba
, Bb
)。
当你调用display()
并计算c
的值时,将使用Aa
和Ab
,而不是Ba
和Bb
没有什么叫variables重载。 这就是为什么你在这两种情况下得到相同的结果。
原因是Java使用词法作用域的概念来实现可变分辨率。
从根本上说,解决函数中的自由variables有两种可能的select('free'表示不是本地的,并且不绑定到函数参数):
1)针对宣布function的环境
2)针对执行function的环境(称为)
Java是第一种方式,所以方法中的自由variables( 静态地,在编译期间 )与它们的词法范围(环境)解决,其中包括:
- 方法参数和局部方法variables
- 包含方法声明的类中的字段声明
- 父类中的公共字段声明
- 等等,inheritance的链条
你会看到这种行为在大多数编程语言中实现,因为它对开发人员是透明的,并有助于防止variables的阴影出现错误。
这与方法在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" } }
这被称为dynamic分派:方法调用是在运行时针对实际对象在其上调用的dynamic分配的。
当你编译你的代码时,它几乎变成:
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(); } }
因此,当超级调用时, class A
的方法正在被调用。
现在去class A
方法。 这里int c = a + b;
意思是c = this.a + this.b;
这是2 + 3。
结果是5。
B类声明B
范围中的variables, public void display()
是A
类的一部分,只知道它自己的范围variables。
这是inheritance函数给出输出5,5
。
Java没有像variables重写的任何东西。 因此,当方法display()被调用时,它访问父类“A”内部的variables而不是子类“B”内部的variables。
它可以解释为什么你不能打印超类方法内的子类(而不是超类)中声明的variables。 超类方法根本无法访问子类variables。
但是,如果您对两个类中的字段都有访问方法,并且使用这些访问方法获取值而不是直接使用variables名访问,则可以打印5,11。 (即使display()方法只存在于超类中)。 这是因为重写的访问器方法被调用(第二种情况),它从子类返回值。