1. 程式人生 > 其它 >Java類初始化、變數初始化

Java類初始化、變數初始化

Java類初始化、變數初始化

摘自:https://blog.csdn.net/mocas_wang/article/details/109956014

基礎知識

  • Java常量, final 修飾,值被設定後不能再被修改
  • 靜態變數裡, static 修飾,顧名思義,無須建立物件,便可在記憶體中申請一個儲存空間進行儲存
  • 成員變數, 也稱例項變數,它隨著當前物件的建立而建立,隨著物件的銷燬而銷燬,存在於物件所在的堆記憶體中
  • 構造器,建立class物件時執行
  • 靜態初始化塊 ,執行優先順序高於非靜態的初始化塊,它會在物件裝載到 jvm的時候執行一次,執行完成便銷燬,只能初始化 static 修飾的變數
  • 非靜態初始化塊,執行的時候如果有靜態初始化塊,先執行靜態初始化塊再執行非靜態初始化塊,在每個物件生成時都會被執行一次,它可以初始化類的例項變數。但它會在建構函式執行之前被執行。

類的初始化順序

Java中類初始化順序,依次是(靜態變數、靜態初始化塊)>(變數、初始化塊)>構造器。。

變數的初始化問題

類成員變數初始化

對於類的成員變數。不管程式有沒有顯示的初始化,Java 虛擬機器都會先自動給它初始化為預設值。

規則為:

1、整數型別(byte、short、int、long)的基本型別變數的預設值為0。

2、單精度浮點型(float)的基本型別變數的預設值為0.0f。

3、雙精度浮點型(double)的基本型別變數的預設值為0.0d。

4、字元型(char)的基本型別變數的預設為 “/u0000”。

5、布林性的基本型別變數的預設值為 false。

6、引用型別的變數是預設值為 null。

7、陣列引用型別的變數的預設值為 null。春關鍵陣列變數的例項後,如果沒有沒有顯示的為每個元素賦值,Java 就會把該陣列的所有元素初始化為其相應型別的預設值。

區域性變數初始化

區域性變數宣告以後,Java 虛擬機器不會自動的為它初始化為預設值。因此對於區域性變數,必須先經過顯示的初始化,才能使用它。

如果編譯器確認一個區域性變數在使用之前可能沒有被初始化,編譯器將報錯。

那麼,加了修飾的java成員變數是如何初始化的呢?在何時?在程式中的什麼位置?

public class TestC {
	/*
	 * 定義成員變數
	 * 尾數為1表示定義時進行初始化賦值
	 * 尾數為2表示在程式碼塊中進行初始化賦值
	 * 尾數為3表示在建構函式中進行初始化賦值
	 * 尾數為4表示在靜態程式碼塊中進行初始化賦值
	 * 尾數為5表示不初始化賦值
	 */
	/*
	 * 普通成員變數 
	 */
	int field_a1 = 5;
	int field_a2;
	int field_a3;
	//報錯:不能再靜態程式碼塊中使用非靜態變數。 Cannot make a static reference to the non-static field field_a4
	//int field_a4;
	int field_a5;
	/*
	 * final 成員變數
	 */
	final int  field_b1 = 5;
	final int  field_b2;
	final int  field_b3;
	//報錯:不能再靜態程式碼塊中使用非靜態變數。Cannot make a static reference to the non-static field field_b4
	//final int  field_b4;
	//報錯:未初始化 。The blank final field field_b5 may not have been initialized
	//final int  field_b5;
	/*
	 * static成員變數
	 */
	static int field_c1 = 5;
	static int field_c2;
	static int field_c3;
	static int field_c4;
	static int field_c5;
	/*
	 * static final 成員變數
	 */
	static final int field_d1 = 5;
	//報錯:未初始化 。The blank final field field_d2 may not have been initialized
	//static final int field_d2;
	//報錯:未初始化 。The blank final field field_d3 may not have been initialized
	//static final int field_d3;
	static final int field_d4;
	//報錯:未初始化 。The blank final field field_d5 may not have been initialized
	//static final int field_d5;
	
	//程式碼塊
	{
		
		field_a2 = 5;
		field_b2 = 5;
		field_c2 = 5;
		//field_d2 = 5;
	}
	
	//靜態程式碼塊
	static{
		//field_a4 = 5;
		//field_b4 = 5;
		field_c4 = 5;
		field_d4 = 5;
	}
	
	//建構函式
	public TestC(){
		field_a3 = 5;
		field_b3 = 5;
		field_c3 = 5;
		//field_d3 = 5;
		
	}
}

然後我們對這個程式生成的.class檔案進行反編譯,看看他是如何執行的。

下面是TestC.jad檔案。反編譯檔案

public class TestC
{

    public TestC()
    {
        field_a1 = 5;
        field_a2 = 5;
        field_c2 = 5;
        field_a3 = 5;
        field_c3 = 5;
    }

    int field_a1;
    int field_a2;
    int field_a3;
    int field_a5;
    final int field_b1 = 5;
    final int field_b2 = 5;
    final int field_b3 = 5;
    static int field_c1 = 5;
    static int field_c2;
    static int field_c3;
    static int field_c4 = 5;
    static int field_c5;
    static final int field_d1 = 5;
    static final int field_d4 = 5;

}
  • 對於不加修飾的普通成員變數,無論我們在什麼地方對其進行初始化賦值,系統都會預設在建構函式中進行賦值。
  • 對於final變數,無論我們在什麼地方進行賦值,系統會預設final變數是在類中進行初始化。
  • 對於static,系統會根據我們的需求,而在不同位置進行初始化。

通過報錯,我們可以發現。

  • final變數必須進行初始化。否則就會報編譯錯誤。The blank final field field_d5 may not have been initialized
  • static成員變數的初始化發生在類被類載入器(classLoader)載入的時候系統會對沒有初始化的靜態成員變數在靜態區進行預設賦值。
  • 普通成員變數的初始化發生在JVM為類生成例項開闢空間的時候進行預設初始化賦值。

總結

final修飾的變數必須顯性進行初始化。初始化有三種方式:

  1. 直接初始化

  2. 動態程式碼塊

  3. 建構函式

  • 如果一個變數既被final修飾又被static修飾,那麼這個變數一定要被初始化(滿足final特性),另外要麼直接初始化要麼在靜態程式碼塊中進行初始化(滿足static特性)。
  • final修飾的變數的初始化先於static修飾的變數,static修飾的變數先於普通變數的初始化。
  • 如果是final級別的,那麼雖然也是根據程式碼次序的前後順序進行初始化,但是它是在編譯階段進行巨集替換,即在編譯階段就知道了值,因此在載入執行階段對這一先後順序完全感知不到。
  • 但是如果是static級別的,程式碼的先後順序會造成程式執行結果的不同。因為它們都是在載入進行的時候進行初始化的。

歸結如下:

  1. final修飾的變數在編譯階段完成
  2. static修飾的變數在一個靜態程式碼塊中進行初始化
  3. 普通變數在建構函式中進行初始化