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()
方法如何工作?
答案 0 :(得分:109)
为什么输出为5,5?
因为A.display()
只知道字段A.a
和A.b
。这些是A
中任何代码都知道的唯一字段。看起来您希望B
中的声明“覆盖”现有的字段声明。他们没有。他们声明了隐藏现有字段的新字段。变量实际上不像方法那样表现 - 覆盖变量的概念根本就不存在。来自the JLS section 8.3:
如果类声明了具有特定名称的字段,那么该字段的声明将被称为 hide 任何和所有可访问的超类中具有相同名称的字段的声明,以及该类的超接口
您可以通过更改B
来获得所需的效果,以便其构造函数更改从A
继承的现有字段的值:
class B extends A {
B() {
a = 5;
b = 6;
}
}
请注意,这些是不是变量声明。他们只是作业。当然在大多数代码中(好吧,我见过的大多数代码)A
中的字段都是私有的,因此无法从B
访问,但这仅仅是为了解释语言行为。
答案 1 :(得分:16)
在课程A
中,您声明了字段a
和b
。方法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);
}
}
答案 2 :(得分:13)
这样做时:
class B extends A
{
int a = 5, b = 6;
}
您不是重新定义 a
和b
,您创建具有相同名称的新变量。因此,您最终会得到四个变量(A.a
,A.b
,B.a
,B.b
)。
当您致电display()
并计算c
的值时,将使用A.a
和A.b
,而不是B.a
和B.b
< / p>
答案 3 :(得分:9)
没有任何称为变量覆盖的东西。这就是为什么你在两种情况下得到相同的结果。
答案 4 :(得分: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"
}
}
这称为动态调度:方法调用在运行时动态解析,对照调用它的实际对象。
答案 5 :(得分: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();
}
}
因此,当超级调用时,正在调用class A
的方法。
现在转到class A
的方法。这里int c = a + b;
表示
c = this.a + this.b;
即2 + 3。
结果是5。
答案 6 :(得分:2)
B类声明B
范围内的变量,public void display()
是A
类的一部分,只知道它自己的范围变量。
答案 7 :(得分:1)
这是继承功能,它提供输出5,5
。
答案 8 :(得分:0)
Java没有像变量覆盖那样的东西。因此,当调用方法display()时,它访问父类中的变量&#39; A&#39;而不是子类中的变量&#39; B&#39;。
可以解释为什么你不能在超类方法中打印在子类(而不是超类)中声明的变量的原因。超类方法根本无法访问子类变量。
但是,如果您对两个类中的字段都有访问器方法,并且使用这些访问器方法来获取值而不是使用变量名直接访问,则您将能够打印5,11。 (即使display()方法仅存在于超类中)。这是因为调用了重写的访问器方法(在第二种情况下),它返回子类中的值。
答案 9 :(得分:0)
为什么输出为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();
}
}
打印