淺談Java對象的初始化順序

當一個類使用new關鍵字來建立新的對象的時候,好比Person per = new Person();JVM根據Person()尋找匹配的類,而後找到這個類相匹配的構造方法,這裏是無參構造,若是程序中沒有給出任何構造方法,則JVM默認會給出一個無參構造。當建立一個對象的時候必定對調用該類的構造方法,構造方法就是爲了對對象的數據進行初始化。JVM會對給這個對象分配內存空間,也就是對類的成員變量進行分配內存空間,若是類中在定義成員變量就賦值的話,就按初始值進行運算,若是隻是聲明沒有賦初始值的話,JVM會按照規則自動進行初始化賦值。而成員方法是在對象調用這個方法的時候纔會從方法區中加載到棧內存中,用完就當即釋放內存空間。java

筆者認爲學到類與對象時的一個關鍵點:初始化順序。 
先假設一個類,如圖中同樣各組成都有,那麼他的初始化順序是函數

一、初始化類變量(即static修飾的成員變量),並未賦值。無論寫的位置在哪裏,只要是類變量,系統總會先找到它進行變量初始化。
二、執行靜態代碼塊和類變量定義式,二者根據寫的位置來決定前後,先寫先執行。其實從某種角度上看,能夠把類變量定義賦值視爲兩部分:一部分是定義變量,一部分賦值。而這個賦值部分能夠看作是一個靜態代碼塊。兩個靜態代碼塊的執行順序天然是看寫的位置的前後了。
三、初始化實例變量(即未被static修飾的成員變量),並未賦值。一樣的,無論寫的位置在哪裏,在建立對象時執行到這步時,系統總會找到它進行變量初始化。
四、執行構造代碼塊和實例變量定義賦值式,二者一樣根據寫的位置前後來決定執行順序前後,一樣能夠按2中所寫來理解。可是,這裏要注意的就是構造代碼塊是能夠調用靜態變量的,實例變量定義賦值式能夠看作是隻對實例變量進行賦值的構造代碼塊。
五、執行構造函數。構造函數一樣能夠調用靜態變量和實例變量。 
初始化結束。 code

這裏說明一點:這是初始化順序,不等同於語句程序的執行過程(畢老師的視頻裏有個很詳細的例子講這個執行過程,不知道的必定要去看)。所以在上面的初始化順序裏沒有成員函數(靜態或者非靜態都沒有),這是由於成員函數都是調用了才執行,雖然靜態函數已經被加載進了方法區,但初始化過程當中並無執行過。 
關於這個初始化順序,其實一句話能夠歸納: 
先初始化類變量而後賦值,再初始化實例變量而後賦值。 
因爲靜態代碼塊能夠調用靜態變量,構造代碼塊和構造函數能夠調用實例變量和靜態變量,這塊很容易來個看似複雜的代碼,將一個變量變來變去的,弄明白這個初始化順序就會解決很快了。 視頻

接下來,看幾個例子來驗證下: 對象

第一個:內存

public class JustForTest {
    public static void main(String[] args) {
        Car c=new Car();
        sop("i="+c.i);
    }
     static void sop(Object obj){
        System.out.println(obj);
    }
}

class Car{
    static int i=1;   //定義賦值
    static {          //靜態代碼塊
        i=4;
    }   
}


運行結果爲:i=4.class

只改寫Car的內部,讓靜態代碼塊和靜態變量的定義賦值互換位置,其餘保持不變:變量

class Car{
    static {          //靜態代碼塊
        i=4;
    }
    static int i=1;   //定義賦值    
}


運行結果爲:i=1.構造函數

最後來個綜合點的,把Car再改寫一下:程序

class Car{
  static int i=1;     //靜態變量定義賦值
  Car(){             //構造函數
  i=2;
  }
  static {          //靜態代碼塊
        i=4;
    }
    {           //構造代碼塊
       i=3;
    }
}


運行結果是:i=2.

按初始化順序,構造函數是最後初始化的。  

相關文章
相關標籤/搜索