据了解,子类getS
中的BB
方法会覆盖父类AA
中的相同方法。然而,尽管两个类都初始化了由s
返回的字段getS
,但它打印为null
。为什么会这样?
这是代码:
public class AA {
public String getS() {
return s;
}
private String s = "hello1";
public AA() {
System.out.println(service() + getS());
}
public static String service() {
return "A service ";
}
}
public class BB extends AA {
private String s = "hello2";
@Override
public String getS() {
return s;
}
public static String service() {
return "B service ";
}
}
public class CC {
public static void main(String[] args) {
BB b =new BB(); //prints "A service null"
}
}
答案 0 :(得分:4)
当我们致电new SomeClass()
new
运算符首先创建SomeClass
的对象,但该对象的所有字段都设置为默认值(0,' \ 0',false,null)。
创建对象后,将对初始化对象执行构造函数的代码(将其字段设置为正确的值,并可能执行其他一些操作)。
但是如果类具有父类,则在我们开始使用可能依赖于那些字段状态的继承方法之前,构造函数首先(隐式或显式)调用其super()
构造函数以确保所有继承的字段都已正确初始化。
但方法是多态的(只要它们不是private
,static
或final
)。这意味着当在超类中时,我们调用被覆盖的方法,"最新版本"代码将被执行(因为多态性使用this
实例的实际类型 - 由new
关键字返回 - 来定位应从中开始搜索应该执行的代码的类 - 这称为迟到或{ {3}})。
因此,您在getS()
超类中调用AA
方法但在BB
实例上调用(因为这是new BB
创建的),来自BB
类的重写代码被执行了。问题是此代码使用s
类中声明的BB
,但s
尚未初始化。让我们看看BB
调用的new BB()
的默认构造函数如何:
BB(){
super(); //calls AA()
s = "hello2"; // by default `s` holds `null` value
// all initialization is moved to constructors
// after superconstructor call
}
所以
s = "hello2";
之前s
(BB类)持有null
s
初始化为"hello2"
之前super()
被调用(内部调用getS()
)getS()
是BB
类的多态代码,因此该方法将被执行BB
)使用的BB s
尚未初始化,因此它保留null
,这就是您在控制台中看到的内容。 因此,在可以覆盖的构造函数方法中,被认为是不好的做法(在大多数情况下)。我们应该限制自己调用非多态的方法,这意味着private
static
或final
。
答案 1 :(得分:2)
Here您可以看到创建对象时初始化的顺序。该答案基于Java Specs
让我复制粘贴
对象初始化
通常,只要创建新对象,就会初始化对象 通过评估类实例创建表达式。这继续下去 如下:
将构造函数的参数分配给新创建的参数 此构造函数调用的变量。
如果此构造函数以显式构造函数调用开头 (§8.8.7.1)同一类中的另一个构造函数(使用此), 然后评估参数并处理构造函数调用 递归地使用这五个相同的步骤。如果那个构造函数 调用突然完成,然后此过程完成 突然出于同样的原因;否则,继续步骤5.
此构造函数不以显式构造函数开头 在同一个类中调用另一个构造函数(使用此方法)。如果 这个构造函数用于Object以外的类,然后是这个 构造函数将以a的显式或隐式调用开始 超类构造函数(使用超级)。评估参数和 使用递归进行超类构造函数调用的过程 这五个步骤相同。如果该构造函数调用完成 突然,这个程序突然完成同样的事情 原因。否则,请继续执行步骤4.
执行实例初始值设定项和实例变量初始值设定项 对于此类,分配实例变量的值 初始化器到相应的实例变量中 从左到右的顺序,它们在源中以文本形式出现 该类的代码。如果执行任何这些初始化程序 导致异常,然后不再处理初始化器 并且此过程突然以相同的异常完成。 否则,请继续步骤5.
- 醇>
执行此构造函数的其余部分。如果执行 突然完成,然后这个程序突然完成 同样的道理。否则,此过程正常完成。
执行新的BB()时,将调用默认构造函数
public BB() {
super();
}
super()
在您的代码中声明为
public AA() {
System.out.println(service() + getS());
}
service()
是一个静态方法,无法覆盖,因此调用A.service()
方法,并将其结果与调用覆盖方法的结果B.getS()
此时(参见5),成员B.s
尚未初始化,其值为null
。
这些是获得
的原因A service null
答案 2 :(得分:1)
这是因为类层次结构。运行时
System.out.println(service() + getS());
它调用重写的getS()方法。因为它打印了在类BB中声明的属性(它不在类AA中访问s属性,因为它是私有的)。
此时(在AA类中运行构造函数时)BB类中的属性未初始化。因此它打印为null。
答案 3 :(得分:1)
原因是BB没有构造函数本身。 new BB()调用 public AA(),然后执行以下语句:
System.out.println(service() + getS());
然后在 A中调用,所以:
服务:A
getS():返回s(null)的默认值,
调用构造函数意味着尚未创建对象,然后属性的初始化仍然为null。