類的生命週期

引言:

java虛擬機爲java程序提供運行時環境,其中一項重要的任務就是管理類和對象的生命週期。類的生命週期從類被加載、連接和初始化開始,到類被卸載結束。當類出於生命週期中時,它的二進制數據位於方法區內,在堆區內還會有一個相應的描述這個類的Class對象。只有當類處於生命週期中時,java程序才能使用它,比如調用類的靜態屬性和方法,或者創建類的實例。


java虛擬機及程序的生命週期


當通過java命令運行一個java程序時,就啓動了一個java虛擬機進程。java虛擬機進程從啓動到終止的過程,成爲java虛擬機的生命週期。在一下情況下,java虛擬機將結束生命週期:

1、程序正常執行結束;

2、程序在執行中因爲出現異常或錯誤而異常終止;

3、執行了System.exit()方法;

4、由於操作系統出現錯誤而導致java虛擬機進程終止。

當java虛擬機處於生命週期中時,它的任務就是運行java程序。java程序從開始運行到終止的過程爲程序的生命週期,它和java虛擬機的生命週期是一致的。


類的加載、連接和初始化

當java程序需要使用某個類時,java虛擬機會確保這個類已經被加載、連接和初始化。其中連接的過程又包括驗證、準備和解析這三個步驟。

加載:查找並加載類的二進制數據

           類的加載是指把類的.class文件中的二進制數據讀入到內存中,把它存放在運行時數據區的方法區內。然後在堆區創建一個java.lang.Class對象,用來封裝類在方法區內的數據結構。類的加載的最終產品是位於運行時數據區的堆區的Class對象。Class對象封裝了類在方法區內的數據結構,並且向java程序提供了訪問類在方法區內的數據結構的接口。


Class對象是java程序與類在方法區的數據結構的接口

類的加載是由類加載器完成的。類加載器並不需要等到某個類被“首次主動使用”時再加載它,java虛擬機規範允許類加載器在預料某個類將要被使用時就預先加載它,如果加載類的過程中遇到.class文件缺失或存在錯誤,類加載器必須等到程序首次主動使用該類時才報告錯誤,如果這個類一直沒有被使用,那麼類加載器將不會報告錯誤。


類被加載後,進入連接階段。連接就是把已經讀入到內存的類的二進制數據合併到虛擬機的運行時環境中去。連接的第一步就是類的驗證,保證被加載的類有正確的內部結構,並且與其它類協調一致,如果java虛擬機檢查到錯誤,會拋出相應的Error對象。

也許你會問:由java編譯器生成的java類的二進制數據肯定是正確的,爲什麼還要進行類的驗證呢?這是因爲java虛擬機並不知道某個特定的.class文件到底是如何被創建的,可能是正常的java編譯器生成的,也有可能是黑客特質的(黑客試圖破壞虛擬機的環境),類的驗證能提供程序的健壯性,確保程序被安全的執行。

連接:包括驗證、準備、解析類的二進制數據

        1、驗證:確保被加載類的正確性

              類的驗證主要包括:

                 a、類文件的結構檢查,確保類文件遵從java類文件的固定格式

                 b、語義檢查,確保類本身符合java語言的語法規定,比如驗證final類是不是有子類,final 類型的方法有沒有被覆蓋等等。

                  c、二進制兼容的驗證,確保互相引用的類之間的協調一致。比如A類的getOne()方法中會調用B類的run()方法。java虛擬機在驗證A類的時候會檢查在方法區內是否存在B類的run方法,如果不存在(A類和B類的版本不兼容,就會出現這種問題),就會拋出NoSuchMethodError錯誤。

         2、準備:爲類的靜態變量分配內存,並將其初始化爲默認值

               在準備階段,java虛擬機爲類的靜態變量分配內存,並設置默認的初始值。例如對於下面的Sample類,在準備階段,將爲int類型的靜態變量a分配4個字節的內存空間,並且賦予默認值0,爲long類型的靜態變量b分配8個字節的內存空間,並且賦予默認值0。

public class Sample {

	private static int a = 1;
	public static long b;
	
	static {
		b = 2;
	}
}


         3、解析:把類中的符號引用轉換爲直接引用

              在解析階段,java虛擬機會把類的二進制數據中的符號引用替換爲直接引用。例如Sample類的gotoWork()方法中會引用SampleTest類的run()方法。

public class Sample {

	private static int a = 1;
	public static long b;
	
	static {
		b = 2;
	}
	public void gotoWork(){
		SampleTest test = new SampleTest();
		test.run();//這段代碼在Sample類的二進制數據中表示爲符號引用。
	}
}

在Sample類的二進制數據中,包含了一個對SampleTest類的run()方法的符號引用,它由run()方法的全名和相關描述符組成。在解析階段,java虛擬機會把這個符號引用替換成一個指針,該指針指向SampleTest類的run()方法在方法區內的內存位置,這個指針就是直接引用。

初始化:給類的靜態變量賦予正確的初始值

 初始化階段,java虛擬機執行類的初始化語句,爲類的靜態變量賦予初始值。在程序中,靜態變量的初始化有兩種途徑:

1、在靜態變量的聲明處進行初始化;

2、在靜態代碼塊處進行初始化,例如以下代碼,靜態變量a和b都被顯示的初始化,而靜態變量c沒有被顯示初始化,一直保持默認值0。

	private static int a = 1;
	public static long b;
	public static long c;
	
	static {
		b = 2;
	}
}

靜態變量的聲明語句,以及靜態代碼塊都被看作類的初始化語句,java虛擬機會按照初始化語句在類文件中的先後順序來一次執行它們。例如當以下Sample類被初始化後,靜態變量a的值爲4。

public class Sample {

	private static int a = 1;
	public static long b;
	public static long c;

	static {
		b = 2;
		a = 3;
	}
	static {
		a = 4;
	}

	public static void main(String[] args) {
		System.out.println("a = " + a);
	}

}

java虛擬機初始化一個類包含以下步驟:

1、假如這個類還沒有被加載和連接,那就先進行加載和連接。

2、假如類存在直接的父類,並且父類還未被初始化,那就先初始化父類

3、加入類存在初始化語句,那就依次執行初始化語句。

當初始化一個父類時,也需要重複以上步驟,這會確保當程序主動使用一個類時,這個類及所有父類(包括直接父類和間接父類)都已經被初始化。程序中第一個被初始化的類是Object類。例如以下代碼中,Sub類是Base類的子類,因此當初始化Sub類的時候,會先初始化Base類。

class Base{
	static int a = 1;
	static {
		System.out.println("init Base");
	}
}
class Sub extends Base{
	static int b = 1;
	static {
		System.out.println("init Sub");
	}
}

public class InitTester {

	static {
		System.out.println("init InitTester");
	}
	public static void main(String[] args) {
		System.out.println("b="+Sub.b);//執行這行代碼時,先一次初始化Base類和Sub類
	}
}

當運行InitTester類的main方法時,當訪問Sub.b的時候,先依次初始化Base類和Sub類。以上程序執行結果爲:

init InitTester
init Base
init Sub
b=1


如果把以上main方法做如下修改:

public static void main(String[] args) {
		Base base;//不會初始化Base類
		System.out.println("After defining base");
		base = new Base();//執行這行代碼時,初始化Base類
		System.out.println("After createing an object of Base");
		System.out.println("a = "+base.a);
		System.out.println("b = "+Sub.b);//執行這行代碼時,僅僅初始化Sub類,因爲Base類已經初始化過了
	}

所以以上程序的打印結果爲:

init InitTester
After defining base
init Base
After createing an object of Base
a = 1
init Sub
b = 1




發佈了33 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章