Java:为什么由 super class 初始化的变量在创建对象后保持值
Java: Why a variable initialised by super class keeps value once object is created
我在工作中提出了一个问题,并简化了代码以重现它
public class Test {
public void callTest(){ return; }
public Test() { callTest(); }
}
public class Test1 extends Test{
private Integer myInt;
private Integer myNullInt = null;
public Test1() {
super();
}
@Override
public void callTest() {
myInt = 25;
myNullInt = 30;
}
@Override
public String toString() {
return "Test1{myInt=" + myInt + ", myNullInt=" + myNullInt +'}';
}
public static void main(String[] args) {
Test1 t1 = new Test1();
System.out.println(t1);
}
}
结果是:Test1{myInt=25, myNullInt=null}
根据我的理解,实例变量初始值设定项应该在调用 super() 之后执行。
那么为什么 'myInt' 得到一个值。
甚至这怎么可能?
您的代码中的执行顺序如下所示。
Test1
的构造函数被调用。
super
调用 Test
. 中的构造函数
- 由于调用是从
Test1
开始的,因此调用了被覆盖的 callTest
,它将 myInt
的值设置为 25,将 myNullInt
的值设置为 30。
super
已完成,现在继续创建 Test1
对象。
myNullInt
的实例变量被覆盖为 null。由于myInt
没有指令,所以数值不变。
sysout
调用调用 toString
并打印 Test1
值。
声明实例变量的方式在继承中很重要。
实例初始化程序在调用 super();
之后执行 - 这就是 myNullInt
为空的原因。 myInt
不涉及实例初始值设定项。
事件的顺序是这样的:
- main 方法创建一个
new Test1()
对象
- 也就是说调用了
Test1
的构造函数
- 该构造函数依次调用
super
构造函数 Test()
Test
的构造函数调用callTest();
方法
- 由于正在构建的对象是
Test1
对象,所以调用Test1
中的callTest
方法
Test1.callTest()
将 myInt
字段设置为 25,将 myNullInt
字段设置为 30
- 之后
Test
的构造函数结束
Test1
的构造函数恢复并执行字段初始化程序 myNullInt = null;
- 因为
myInt
没有初始化器,所以它的值没有改变
Java 语言规范中详细描述了该过程(包括使用默认值初始化),Creation of New Class Instances:
Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class type and all the instance variables declared in each superclass of the class type, including all the instance variables that may be hidden (§8.3).
[If there is not sufficient space ...] Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§4.12.5).
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:
- / 2. [...]
- [...] an explicit or implicit invocation of a superclass constructor [...]
- Execute the instance initializers and instance variable initializers for this class [...]
- Execute the rest of the body of this constructor. [...]
我在工作中提出了一个问题,并简化了代码以重现它
public class Test {
public void callTest(){ return; }
public Test() { callTest(); }
}
public class Test1 extends Test{
private Integer myInt;
private Integer myNullInt = null;
public Test1() {
super();
}
@Override
public void callTest() {
myInt = 25;
myNullInt = 30;
}
@Override
public String toString() {
return "Test1{myInt=" + myInt + ", myNullInt=" + myNullInt +'}';
}
public static void main(String[] args) {
Test1 t1 = new Test1();
System.out.println(t1);
}
}
结果是:Test1{myInt=25, myNullInt=null}
根据我的理解,实例变量初始值设定项应该在调用 super() 之后执行。 那么为什么 'myInt' 得到一个值。 甚至这怎么可能?
您的代码中的执行顺序如下所示。
Test1
的构造函数被调用。super
调用Test
. 中的构造函数
- 由于调用是从
Test1
开始的,因此调用了被覆盖的callTest
,它将myInt
的值设置为 25,将myNullInt
的值设置为 30。 super
已完成,现在继续创建Test1
对象。myNullInt
的实例变量被覆盖为 null。由于myInt
没有指令,所以数值不变。sysout
调用调用toString
并打印Test1
值。
声明实例变量的方式在继承中很重要。
实例初始化程序在调用 super();
之后执行 - 这就是 myNullInt
为空的原因。 myInt
不涉及实例初始值设定项。
事件的顺序是这样的:
- main 方法创建一个
new Test1()
对象 - 也就是说调用了
Test1
的构造函数 - 该构造函数依次调用
super
构造函数Test()
Test
的构造函数调用callTest();
方法- 由于正在构建的对象是
Test1
对象,所以调用Test1
中的callTest
方法 Test1.callTest()
将myInt
字段设置为 25,将myNullInt
字段设置为 30- 之后
Test
的构造函数结束
Test1
的构造函数恢复并执行字段初始化程序myNullInt = null;
- 因为
myInt
没有初始化器,所以它的值没有改变
Java 语言规范中详细描述了该过程(包括使用默认值初始化),Creation of New Class Instances:
Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class type and all the instance variables declared in each superclass of the class type, including all the instance variables that may be hidden (§8.3).
[If there is not sufficient space ...] Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§4.12.5).
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:
- / 2. [...]
- [...] an explicit or implicit invocation of a superclass constructor [...]
- Execute the instance initializers and instance variable initializers for this class [...]
- Execute the rest of the body of this constructor. [...]