Java有自動的記憶體回收行程,不需要我們手動清理對象,但卻需要我們手動建立對象。那麼,對於一個對象來說,它的建立過程或者說初始化過程是怎樣進行的,我們在設計自己的類時又要如何保證對象的初始化順利進行。
基本的初始化過程如下:
- 當首次建立類型為MyClass的對象(構造器可看成靜態方法),或首次調用(訪問)MyClass類的靜態方法(待用資料域),Java解譯器尋找類路徑,定位MyClass.class檔案;
- 載入MyClass.class,建立一個相應的Class對象,執行此類中的所有靜態初始化動作,靜態初始化只在Class對象首次載入時進行一次;若此對象有父類,則先執行上述動作;
- 當用new建立MyClass對象時,將在堆上為MyClass對象分配足夠的儲存空間,這Block Storage空間會被清零,這就自動將MyClass對象中的所有基本類型資料設定成預設值,對象引用設定成null;若此對象有父類,則先執行上述動作;
- 按照類成員域的定義順序執行初始化工作,然後調用構造方法;若此對象有父類,則先執行上述動作。
下面將對不同的操作進行詳細介紹。 非繼承對象初始化
這裡的對象普通類的執行個體,普通類即形如[修飾符] class MyClass {}的類,沒有繼承任何類(當然肯定是繼承自Object類)。此類類對象的初始化分兩種情況——訪問類的靜態成員(MyClass.*)和建立一個對象執行個體(new)。
這裡定義了一個Parent類作為測試類別:
class Parent { public static final String PARENTSTATICFIELD1; static{ System.out.println("before Parent's 靜態初始化塊"); System.out.println(toStaticString()); PARENTSTATICFIELD1="PARENT"; System.out.println("after Parent's 靜態初始化塊"); System.out.println(toStaticString()); } private static String parentStaticField2=initStaticField2(); protected String name=""; protected String id; { System.out.println("before Parent's 執行個體初始化塊1"); System.out.println(toString()); old=30; System.out.println("after Parent's 執行個體初始化塊1"); System.out.println(toString()); } protected int old=40; { System.out.println("before Parent's 執行個體初始化塊2"); System.out.println(toString()); name="parent"; System.out.println("after Parent's 執行個體初始化塊2"); System.out.println(toString()); } public Parent(){ System.out.println("before Parent's 構造方法"); System.out.println(toString()); id="1001100110011001"; System.out.println("after Parent's 構造方法"); System.out.println(toString()); } protected static String initStaticField2(){ System.out.println("parentStaticField2 初始化"); return "parent"; } public static String toStaticString(){ return "PARENTSTATICFIELD1="+PARENTSTATICFIELD1+ ", parentStaticField2="+parentStaticField2; } @Override public String toString(){ return "name="+name+", id="+id+", old="+old; }}
訪問類的靜態成員
在僅訪問類的靜態成員時,初始化過程如下:
- Java解譯器尋找類路徑,定位.class檔案,尋找到後載入.class檔案,並在虛擬機器中建立一個相應的Class對象;
- 按照類中待用資料域和靜態初始化塊的定義順序,依次執行初始化過程;
- 任何類的靜態初始化過程只執行一次。
String s=Parent.PARENTSTATICFIELD1;// output:// before Parent's 靜態初始化塊// PARENTSTATICFIELD1=null, parentStaticField2=null// after Parent's 靜態初始化塊// PARENTSTATICFIELD1=PARENT, parentStaticField2=null// parentStaticField2 初始化
建立一個對象執行個體
如果在建立一個對象執行個體之前已經訪問過此類的某個靜態成員,則不會重複執行靜態初始化過程,否則將先執行靜態初始化過程,然後執行下列步驟:
- 在堆上為類對象分配足夠的儲存空間,這Block Storage空間會被清零,這就自動將類對象中的所有基本類型資料設定成預設值,對象引用設定成null;
- 按照類中非待用資料域和非靜態初始化塊的定義順序,依次執行初始化過程;
- 調用相應的構造方法;
- 每次建立類對象時,上述步驟都要執行一遍。
new Parent();// output:// before Parent's 靜態初始化塊// PARENTSTATICFIELD1=null, parentStaticField2=null// after Parent's 靜態初始化塊// PARENTSTATICFIELD1=PARENT, parentStaticField2=null// parentStaticField2 初始化// before Parent's 執行個體初始化塊1// name=, id=null, old=0// after Parent's 執行個體初始化塊1// name=, id=null, old=30// before Parent's 執行個體初始化塊2// name=, id=null, old=40// after Parent's 執行個體初始化塊2// name=parent, id=null, old=40// before Parent's 構造方法// name=parent, id=null, old=40// after Parent's 構造方法// name=parent, id=1001100110011001, old=40
或者:
String s=Parent.PARENTSTATICFIELD1; System.out.println("建立Parent對象"); new Parent();// output:// before Parent's 靜態初始化塊// PARENTSTATICFIELD1=null, parentStaticField2=null// after Parent's 靜態初始化塊// PARENTSTATICFIELD1=PARENT, parentStaticField2=null// parentStaticField2 初始化// 建立Parent對象// before Parent's 執行個體初始化塊1// name=, id=null, old=0// after Parent's 執行個體初始化塊1// name=, id=null, old=30// before Parent's 執行個體初始化塊2// name=, id=null, old=40// after Parent's 執行個體初始化塊2// name=parent, id=null, old=40// before Parent's 構造方法// name=parent, id=null, old=40// after Parent's 構造方法// name=parent, id=1001100110011001, old=40
繼承對象初始化
現在考慮具有繼承層次的類對象,即形如[修飾符] class MyClass2 extends MyClass1的類對象(MyClass1不是Object類)。定義Child類作為第二個測試類別:
class Child extends Parent { public static final String CHILDSTATICFIELD1; static{ System.out.println("before Child's 靜態初始化塊"); System.out.println(toStaticString()); CHILDSTATICFIELD1="CHILD"; System.out.println("after Child's 靜態初始化塊"); System.out.println(toStaticString()); } private static String childStaticField2=initStaticField2(); { System.out.println("before Child's 執行個體初始化塊"); System.out.println(toString()); name="child"; old=15; System.out.println("after Child's 執行個體初始化塊"); System.out.println(toString()); } public Child(){ System.out.println("before Child's 構造方法"); System.out.println(toString()); id="0110011001100110"; System.out.println("after Child's 構造方法"); System.out.println(toString()); } protected static String initStaticField2(){ System.out.println("childStaticField2 初始化"); return "child"; } public static String toStaticString(){ return "CHILDSTATICFIELD1="+CHILDSTATICFIELD1+ ", childStaticField2="+childStaticField2; }}
訪問父類的靜態成員
當僅訪問父類的靜態成員時,此時僅進行父類的靜態初始化過程。
String s=Child.PARENTSTATICFIELD1;\\ output:\\ before Parent's 靜態初始化塊\\ PARENTSTATICFIELD1=null, parentStaticField2=null\\ after Parent's 靜態初始化塊\\ PARENTSTATICFIELD1=PARENT, parentStaticField2=null\\ parentStaticField2 初始化
訪問子類的靜態成員
當僅訪問子類的靜態成員時,先進行父類的靜態初始化過程,再執行子類的靜態初始化過程。
String s=Child.CHILDSTATICFIELD1;// output:// 父類的靜態初始化過程// before Child's 靜態初始化塊// CHILDSTATICFIELD1=null, childStaticField2=null// after Child's 靜態初始化塊// CHILDSTATICFIELD1=CHILD, childStaticField2=null// childStaticField2 初始化
建立子類的對象執行個體
如果在建立子類的對象執行個體之前已經訪問過父類的某個靜態成員,則父類的靜態初始化過程不重複執行;如果在建立子類的對象執行個體之前已經訪問過子類的某個靜態成員,則父類和子類的靜態初始化過程不重複執行。在靜態初始化過程之後需要執行如下步驟:
- 執行父類的非靜態初始化過程和父類的構造方法;
- 執行子類的非靜態初始化過程和子類的構造方法。
String s=Child.PARENTSTATICFIELD1; System.out.println("建立Child對象"); new Child();// output:// 父類的靜態初始化過程// 建立Child對象// 子類的靜態初始化過程before Parent's 執行個體初始化塊1name=, id=null, old=0after Parent's 執行個體初始化塊1name=, id=null, old=30before Parent's 執行個體初始化塊2name=, id=null, old=40after Parent's 執行個體初始化塊2name=parent, id=null, old=40before Parent's 構造方法name=parent, id=null, old=40after Parent's 構造方法name=parent, id=1001100110011001, old=40before Child's 執行個體初始化塊name=parent, id=1001100110011001, old=40after Child's 執行個體初始化塊name=child, id=1001100110011001, old=15before Child's 構造方法name=child, id=1001100110011001, old=15after Child's 構造方法name=child, id=0110011001100110, old=15
或者:
String s=Child.CHILDSTATICFIELD1; System.out.println("建立Child對象"); new Child();// output:// 父類的靜態初始化過程// 子類的靜態初始化過程建立Child對象// 父類的非靜態初始化過程和構造方法// 子類的非靜態初始化過程和構造方法
或者:
new Child();// output:// 父類的靜態初始化過程// 子類的靜態初始化過程// 父類的非靜態初始化過程和構造方法// 子類的非靜態初始化過程和構造方法
對象清理
Java允許在類中定義一個名為finalize()的方法:一旦記憶體回收行程準備釋放對象佔用的儲存空間,將先調用finalize()方法,並在下一次記憶體回收動作發生時才真正回收對象佔用的記憶體。
Java中的對象並非總是被記憶體回收:對象可能不被記憶體回收;記憶體回收不等於“析構”;記憶體回收只與記憶體有關。
無論是“記憶體回收”還是“終結”,都不保證一定發生,如果Java虛擬機器並未面臨記憶體耗盡的情形,它是不會浪費時間去執行記憶體回收以恢複記憶體的。
System.gc()用於強制進行終結動作。
class Finalize { public Finalize(){} @Override public void finalize(){ System.out.println("Finalize's finalize"); }}
“`java
Finalize f=new Finalize();
f=null;
System.out.println(“after set f=null”);
System.gc();
System.out.println(“after invoke System.gc()”);
// output:
// after set f=null
// after invoke System.gc()
// Finalize’s finalize