1、使用初始化塊
[修飾符]{
//初始化塊的可執行性代碼
}
初始化塊雖然也是Java類的一種成員,但它沒有名字,也就沒有標識,因此無法通過類、對象來調用初始化塊。初始化塊只在創建Java對象時隱式執行,而且在執行構造器之前執行。
普通初始化塊、聲明實例變量指定的默認值都可以是對象的初始化代碼,他們的執行順序與源代碼中的排列順序相同。如下代碼
public class InstanceInitTest { { a = 6; } int a = 9; public static void main(String[] args) { // TODO Auto-generated method stub //輸出結果為9 System.out.println(new InstanceInitTest().a); } }
當Java創建一個對象時,系統先為該對象的所有實例變量分配內存,接着程序開始對這些實例變量進行初始化,其初始化的順序是:先執行初始化塊或聲明實例變量時指定的初始值(這兩個地方指定初始值的執行允許與它們在源代碼中的排列順序相同),再執行構造器力指定的初始值。
如果兩個構造器中有相同的初始化代碼,且這些初始化代碼無須接受參數,就可以把它們放在初始化塊中定義。
2、靜態初始化塊
如果定義初始化塊時使用了static修飾符,則這個初始化塊就變成了靜態初始化塊。靜態初始化塊時類相關的,系統將在類初始化階段執行靜態初始化塊,而不是在創建對象時才執行。因此靜態初始化塊總是比普通初始化塊先執行。
下面創建三個類:Root、Mid和Leaf,這三個類都提供了靜態初始化塊和普通初始化塊,而Mid類中還使用了this調用重載的構造器,而Leaf使用了super顯示調用父類指定的構造器。
class Root { static{ System.out.println("Root的靜態初始化塊"); } { System.out.println("Root的普通初始化塊"); } Root() { System.out.println("Root的無參構造函數"); } } class Mid extends Root { static{ System.out.println("Mid的靜態初始化塊"); } { System.out.println("Mid的普通初始化塊"); } public Mid() { System.out.println("Mid的無參構造函數"); } public Mid(String msg) { this(); System.out.println("Mid的帶參構造函數,其參數值為:"+msg); } } class leaf extends Mid { static{ System.out.println("leaf的靜態初始化塊"); } { System.out.println("leaf的普通初始化塊"); } public leaf() { System.out.println("leaf的無參構造函數"); } } public class InstanceInitTest { public static void main(String[] args) { // TODO Auto-generated method stub new leaf(); new leaf(); } }
從結果來看,第一次創建一個leaf對象時,因為系統中還不存在Leaf類,因此需要先加載並初始化Leaf類,初始化Leaf類時先執行其頂層父類的靜態初始化塊,再執行其直接父類的靜態初始話塊,最后才執行Leaf本身的靜態初始話塊。
一旦初始化Leaf類初始化后,Leaf類在該虛擬機力將一直存在,因此當第二次創建Leaf實例時無需再次對Leaf類進行初始化。
普通初始化塊和構造器的執行順序與前面介紹的一致。
靜態初始化塊和聲明靜態成員變量時所指定的初始化值都是該類的初始化代碼,它們的執行順序與源代碼中的排序順序相同。