示例代码:
public class A {
public int number;
public A(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
}
public class B extends A{
public int number;
public B(int number) {
super(number);
}
public int getNumber() {
return number;
}
}
public class C {
public static void main(String args[]) {
A test1 = new B(2);
B test2 = new B(2);
System.out.println(test1.number) // prints 2
System.out.println(test2.number) // prints 0
System.out.println(test1.getNumber()) //prints 0
System.out.println(test2.getNumber()) // prints 0
}
}
如上所示,test1.number不等于test1.getNumber()。
因此,当我使test1为A类型test1.number的对象时,它引用的是A类中的整数。
但是当我调用test1.getNumber()时,它正在B类中调用getNumber()吗?
为什么会这样?
答案 0 :(得分:0)
根据该网站,我发现了一些信息,这些信息解释了Java中的类层次结构。 当扩展一个类时,基本上是从该类复制代码并将其放入扩展它的代码中,因此不必重写方法,而是在类B中进行操作。因此,与其在A类,它将使用您编码到B类中的那个。
https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
答案 1 :(得分:0)
Java中的所有方法都是虚拟的。一秒钟之后,我会明白这是什么意思。
因此,在这一行:
A test1 = new B(2);
您已经为声明为持有B
的变量分配了类A
的新实例。
使用test1.number
时,您使用的是在类number
中声明的A
变量,假设B
调用super(number)
的构造函数将是2。
但是,当您调用test1.getNumber()
时,这就是虚函数插入的地方。虚函数表示它将始终在构造的类中调用一个方法,而不是您声明它的变量类型。换句话说,它实际上不是调用A
的{{1}},而是像您想的那样调用getNumber
的{{1}}。 B
的构造函数没有为getNumber
的{{1}}变量赋值,因此您得到0。
答案 2 :(得分:0)
这是因为Java允许覆盖方法,但不允许覆盖字段。
规范writes:
如果一个类用某个名称声明了一个字段,则称该字段的声明隐藏了超类和该类的超接口中所有相同名称的字段的所有可访问声明。
在这方面,字段的隐藏与方法的隐藏(第8.4.8.3节)不同,因为在字段隐藏中静态字段和非静态字段之间没有区别,而静态字段和非静态字段之间则有区别方法隐藏中的静态方法。
如果隐藏字段是静态的,则可以使用限定名称对其进行访问(第6.5.6.6.2节),或者使用包含关键字super(第15.11.2节)或强制转换为超类类型的字段访问表达式进行访问。
在这方面,字段的隐藏类似于方法的隐藏。
也就是说,B
的每个实例都有两个不同的字段,它们碰巧共享相同的名称。当您在诸如test1.number
之类的字段访问表达式中使用字段名称时,将根据test1
的编译时间类型来解释该字段名称。
相比之下,实例方法can be overridden:
在类C中声明(或由其继承)的实例方法mC,从C重写在类A中声明的另一方法mA,前提是以下所有条件均成立:
C是A的子类。
...
mC的签名是mA签名的子签名(第8.4.2节)。
(其中“子签名”表示方法名称和参数兼容。)
否则,将调用一个实例方法,并且有一个目标引用。如果目标引用为null,则此时将抛出NullPointerException。否则,目标引用被认为是引用目标对象,并将在调用的方法中用作关键字this的值。
...
否则,可能会发生覆盖。使用动态方法查找。动态查找过程从S类开始,确定如下:
如果调用方式是接口方式或虚拟方式,则S最初是目标对象的实际运行时类R。
...
动态方法查找使用以下过程搜索类S,然后根据需要搜索方法m的类S的超类和超接口。
也就是说,如果您编写test1.getNumber()
,则运行时将评估test1
以获取目标对象,确定其类,然后在该类中寻找合适的方法。
在您的示例中,test1
引用了类B
的对象,这就是调用B.getNumber()
的原因。
然后B.getNumber()
的实现继续读取B.number
,它从未被分配,因此仍包含其默认值0。