基於《Java編程思想》第四版程序員
在C++中經過構造函數和析構函數來保證:對象在使用前被正確初始化,在使用後被正確回收。Java中一樣存在構造函數,可是沒有析構函數。之因此沒有析構函數是由於對象實際的存儲期由GC決定,程序員沒法明確析構函數什麼時候會被執行。編程
GC會在回收對象前執行Object
的protected void finalize()
方法,子類能夠經過重寫finalize()
方法來清理資源。可是由於GC回收對象時間的不肯定性,加上finalize()
自己可能引入的問題,因此並不同意使用該方法。不過Java還提供下面的格式,保證在finally
裏的語句必然在函數正常返回或者拋異常返回前執行,所以能夠將資源的回收放在該語句塊中。函數
try{ }finally{ }
在C++中,類對象實例化時,是不會初始化對象空間的,徹底依靠構造函數將成員變量設置爲正確的初值,可是在Java中,當獲得對象的空間後會先所有清零,再開始初始化對應成員變量。code
C++初始化成員變量有兩種方式對象
Java則有三種方式資源
經過下面的代碼來加深印象class
class MyType{ MyType(String msg){ System.out.println(msg); } } class Base{ MyType b1; static MyType b2 = new MyType("Base static member use ="); MyType b3 = new MyType("Base non-static member use ="); MyType b4; static MyType b5; { b4 = new MyType("Base non-static member in {}"); b5 = new MyType("Base static member in {}"); } static MyType b6; static { b6 = new MyType("Base static member in static {}"); } Base(){ b1 = new MyType("Base non-static member in Base()"); System.out.println("Base()"); } } class Derived extends Base{ MyType d1; static MyType d2 = new MyType("Derived static member use ="); MyType d3 = new MyType("Derived non-static member use ="); MyType d4; static MyType d5; { d4 = new MyType("Derived non-static member in {}"); d5 = new MyType("Derived static member in {}"); } static MyType d6; static { d6 = new MyType("Derived static member in static {}"); } Derived(){ d1 = new MyType("Derived non-static member in Derived()"); System.out.println("Derived()"); } } public class Main { public static void main(String[] args) { Derived d1 = new Derived(); System.out.println("----"); Derived d2 = new Derived(); } }
代碼的輸出順序以下,將分析也包含在輸出中了變量
// 初始化子類時,會先初始化父類,此時先初始化靜態成員變量。順序:定義時賦值>初始化語句塊。 Base static member use = Base static member in static {} // 父類的靜態成員初始化結束後,開始初始化子類的靜態成員變量。順序:定義時賦值>初始化語句塊。 Derived static member use = Derived static member in static {} // 以上對靜態成員表初始化只在第一次加載class時執行的,後續都不會再執行了。 // 開始初始化父類的非靜態成員變量。順序:定義時賦值>初始化語句塊>構造函數 Base non-static member use = Base non-static member in {} Base static member in {} // 此處的靜態成員變量賦值會在每次類實例化對象時,執行 Base non-static member in Base() Base() // 開始初始化子類的非靜態成員變量。順序:定義時賦值>初始化語句塊>構造函數 Derived non-static member use = Derived non-static member in {} Derived static member in {} Derived non-static member in Derived() Derived() ---- // 第二次實例化對象時,只會執行非靜態成員變量的定義賦值、非靜態初始化語句以及構造函數內賦值 Base non-static member use = Base non-static member in {} Base static member in {} Base non-static member in Base() Base() Derived non-static member use = Derived non-static member in {} Derived static member in {} Derived non-static member in Derived() Derived()
整體順序就是構造函數
若是不實例化對象,而是直接訪問靜態成員變量,那麼只會執行父類靜態成變量的定義賦值和靜態初始化語句塊內的賦值程序
public class Main { public static void main(String[] args) { System.out.println( Derived.b5 ); } } /// 輸出以下 Base static member use = Base static member in static {} null
由於子類可能使用到父類的東西,因此老是先初始化父類再初始化子類。一樣的初始化方法和類型(static或者non-static)的成員變量的初始化順序與其定義順序一致。這些和C++都是同樣的。加載class的細節應該是被隱藏在Java解釋器裏了,經過字節碼看不到完整的流程,這和C++就不同了。