java-实例字段的继承如何在此特定代码中工作?
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()方法将如何工作?
10个解决方案
109 votes
为什么输出结果为5,5?
因为A仅知道字段B和2965104426626843843650。这些是A中任何代码都知道的仅有字段。 您似乎希望B中的声明“覆盖”现有的字段声明。 他们没有。 他们声明了隐藏现有字段的新字段。 变量实际上没有方法那样的行为-根本没有覆盖变量的概念。 从JLS 8.3节开始:
如果类声明了具有特定名称的字段,则称该字段的声明隐藏了超类和该类的超接口中所有相同名称的字段的所有可访问声明。
您可以通过更改A来获得所需的效果,以使其构造函数更改其从B继承的现有字段的值:
class B extends A {
B() {
a = 5;
b = 6;
}
}
请注意,这些不是变量声明。 他们只是任务。 当然,在大多数代码(无论如何,无论如何我已经看过的大多数代码)中,A中的字段都是私有的,因此无法从B中进行访问,但这只是出于说明语言行为的示例。
Jon Skeet answered 2020-07-13T20:22:00Z
16 votes
在类A中,您声明字段a和b。方法display使用这些字段。 在类B中,您声明具有相同名称的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);
}
}
fabian answered 2020-07-13T20:22:20Z
13 votes
执行此操作时:
class B extends A
{
int a = 5, b = 6;
}
您没有重新定义display()和c,而是使用相同的名称创建新变量。 因此,您最终得到四个变量(A.a、A.b、B.a、B.b)。
当您致电display()并计算c的值时,将使用A.a和A.b,而不是B.a和B.b
Andy answered 2020-07-13T20:22:49Z
9 votes
没有所谓的变量覆盖。 因此,在两种情况下您都得到相同的结果。
Amit Sharma answered 2020-07-13T20:23:09Z
6 votes
原因是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"
}
}
这称为动态调度:方法调用是在运行时针对实际对象动态解析的,该对象在其上被调用。
Andrei Tomashpolskiy answered 2020-07-13T20:24:17Z
5 votes
当您编译代码时,它几乎变成了:
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。
Pulah Nandha answered 2020-07-13T20:24:51Z
2 votes
B类在B范围内声明变量,public void display()是A类的一部分,并且仅了解其自身的范围变量。
ST3 answered 2020-07-13T20:25:11Z
1 votes
继承功能使输出5,5。
SkoolBoyAtWork answered 2020-07-13T20:25:30Z
0 votes
Java没有像变量覆盖之类的东西。 因此,当调用方法display()时,它访问父类“ A”内的变量,而不访问子类“ B”内的变量。
可以用为什么您无法打印在超类方法内的子类(而不是超类)中声明的变量的相同原因来解释。 超类方法根本无法访问子类变量。
但是,如果两个类中的字段都有访问器方法,并且使用这些访问器方法获取值,而不是直接使用变量名进行访问,则可以打印5,11。 (即使display()方法仅在超类中存在)。 这是因为调用了重写的访问器方法(在第二种情况下),该方法从子类返回值。
Anirudh Kabde answered 2020-07-13T20:26:00Z
0 votes
为什么输出结果为5,5? 而不是5,11?
只要我们在类层次结构中具有相同的实例变量(也适用于类变量),则变量的最近声明会获得优先权。 在这种情况下,display()方法中a和b的最接近声明是A。 因此,B类的实例变量将被隐藏。 因此,在两种情况下,都将打印5个。
y.display()方法如何工作?
另一种选择是在两个类中都使用getter来获取a和b的值。
class A
{
int a = 2, b = 3;
public int getA() {
return a;
}
public int getB() {
return b;
}
public void display()
{
int c = getA() + getB();
System.out.println(c);
}
}
class B extends A
{
int a = 5, b = 6;
public int getA() {
return a;
}
public int getB() {
return b;
}
}
class Tester
{
public static void main(String arr[])
{
A x = new A();
B y = new B();
x.display();
y.display();
}
}
版画
5
11
Developer answered 2020-07-13T20:26:38Z