許多開發者認為:「調用構造函數後,對象才被實例化」。因為構造函數執行完才能調用實例方法,否則會 NullPointerException。但這是錯誤的。
thispublic class Test {
public Test() {
this.doSomething(); // 構造器內已可操作 this
}
private void doSomething() {
System.out.println("do init");
}
}
說明:進入構造器前,對象實例已創建完畢,構造器僅負責初始化。
public class BaseClass {
public BaseClass() {
System.out.println("create base");
}
}
public class SubClass extends BaseClass {
public SubClass() {
System.out.println("create sub");
}
public static void main(String[] args) {
new SubClass(); // 輸出:create base → create sub
}
}
直觀以為:實例化父類 → 初始化父類 → 實例化子類 → 初始化子類。錯誤!
public class BaseClass {
public BaseClass() {
System.out.println("create base");
init(); // 調用 init()
}
protected void init() { System.out.println("in base init"); }
}
public class SubClass extends BaseClass {
int i = 1024;
String s = "13 leaf";
public SubClass() {
System.out.println("create sub");
init();
}
@Override
protected void init() {
System.out.println("now class: " + this.getClass().getSimpleName());
System.out.println("in SubClass");
System.out.println(i); // 這裡 i 是多少?
System.out.println(s);
}
public static void main(String[] args) {
new SubClass();
}
}
create base
now class: SubClass
in SubClass
0 // i 仍為默認值 0!
null // s 仍為默認值 null!
create sub
now class: SubClass
in SubClass
1024 // i 已初始化
13 leaf // s 已初始化
public SubClass();
aload_0 // this
invokespecial BaseClass() // 1. 調用父類構造器
aload_0
sipush 1024 // 2. 初始化子類字段 i
putfield SubClass.i
aload_0
ldc "13 leaf" // 3. 初始化子類字段 s
putfield SubClass.s
getstatic System.out
ldc "create sub"
invokevirtual println
aload_0
invokevirtual init() // 4. 調用子類 init()
return
private/final/static 方法安全)