編寫java程序時,每建立一個新的對象,都會對對象的內容進行初始化。java
每個類的方法中的局部變量都有嚴格的初始化要求,所以假如寫出下面的程序:程序員
void f(){ int i; i++; }
編譯時就會獲得一條出錯信息,由於java會嚴格檢查方法內部局部變量的初始化,正如《java編程思想》中所說「未初始化的局部變量更有多是程序員的疏忽」編程
可是對於類內部的字段,java並不會嚴格檢查字段是否初始化,由於類的每個基本數據類型成員都保證會有一個初始值,哪怕構造器什麼都沒作。app
其實一個對象的初始化並不只僅只是簡單的調用構造器,在這以前,java已經作了不少工做了。學習
建立一個新的對象,就至關於蘋果樹上長出了一個新的蘋果。只有在這個蘋果長出來的時候,咱們才知道其具體的形狀,顏色,重量,這是每一個蘋果所具備的特性,這些特性就至關於對象中的字段。可是全部的蘋果一樣具備共性,好比說都具備相似的結構,這些性質就至關於對象中的static字段,是全部該類對象所共享的。而構造器,就至關於在採摘下蘋果以後,經過人工的手段對其特性作出一些改變。測試
當java要建立一個新的對象時,首先會查找類路徑,找到對應的類,至關於咱們種下了一顆蘋果樹。spa
此時java會載入這個類,此時類中有關靜態初始化的全部動做都會被執行,即類中帶有static前綴的字段會被分配內存,而後進行初始化的賦值。不管爲這個類new多少個對象,static字段的初始化在整個程序的生命週期中只會進行一次,且有本身獨立的內存位置,爲全部該類的對象所共享。就像咱們知道這棵種下的蘋果樹會長出的東西叫作蘋果,和世界任何一個地方的同類的蘋果樹同樣,雖然蘋果尚未長出來,可是它做爲蘋果所具備的性質是客觀存在的。3d
//例子code
若是當前類是導出類,那麼java編譯器就會從其基類開始加載,分別初始化其中的靜態域;對象
不管如何,static域的初始化都只會進行這一次。
接下來,java編譯器就會爲該具體的對象分配內存空間。內存被分配的時候,對應的內存空間會被所有清0,因此全部的非static字段的初始值都是0或者與0等價的值,對象的引用都被置爲null。這就至關於蘋果樹的果實剛剛準備生長的狀態,一切都是最初的模樣。
下一步,java編譯器就會利用每一個字段的初始化語句對字段進行賦值,若是初始化語句調用了別的對象或者方法,那麼編譯器也會不知疲倦的去找,就是爲了保證在調用構造器以前,字段的初始化語句都執行完成。值得注意的是,類中字段的分配內存空間與賦值的過程是先於構造器以及其餘方法進行的,與代碼中初始化語句與方法的排列順序無關,哪怕包括構造器在內的全部方法都排在字段初始化語句以前,編譯器也會跳過這些方法,先執行初始化語句。
若是當前類是導出類,那麼java編譯器就會從其基類開始執行字段的初始化,逐步向外進行,導出類的加載與初始化賦值都必須在基類的基礎上進行,不能憑空產生。
這個賦值的過程就至關於蘋果樹的果實的生長過程,蘋果的大小,重量,顏色等特性都是在這個過程當中慢慢造成的。
接下來才輪到構造器對對象進行初始化,按照new對象時輸入的參數對每一個對象進行塑造,這個過程就至關於蘋果採摘完成以後,清洗乾淨,修剪枝葉。
測試代碼以下:
1 public class init_Test { 2 public static void main(String args[]) { 3 System.out.println("the first code in main"); 4 } 5 static apple apple1=new apple(); 6 static apple apple2=new apple(); 7 } 8 9 class Test{ 10 static int f(String words) { 11 System.out.println(words); 12 return 1; 13 } 14 } 15 16 class apple{ 17 int value1 =Test.f("init value1"); 18 static int static1=Test.f("static init static1"); 19 apple(){ 20 System.out.println("constructor of class apple"); 21 System.out.println("check static1 ="+static1); 22 System.out.println("check static2 ="+static2); 23 System.out.println("check static3 ="+static3); 24 System.out.println("check value1 ="+value1); 25 System.out.println("check value2 ="+value2); 26 System.out.println("check value3 ="+value3); 27 } 28 int value2=Test.f("init value2"); 29 static int static2=Test.f("static init static2"); 30 static int static3; 31 int value3; 32 }
產生的輸出以下:
static init static1
static init static2
init value1
init value2
constructor of class apple
check static1 =1
check static2 =1
check static3 =0
check value1 =1
check value2 =1
check value3 =0
init value1
init value2
constructor of class apple
check static1 =1
check static2 =1
check static3 =0
check value1 =1
check value2 =1
check value3 =0
the first code in main
接下來讓咱們捋清楚這段代碼的執行過程。
首先進入程序的入口:main方法
要執行main方法,就須要首先加載main方法所在的init_Test類。
加載時會對static域進行初始化,所以控制流會跳過main方法,首先執行下面的兩條初始化語句,對靜態的apple1和apple2進行初始化。也就是建立兩個新的apple對象。
初始化調用了apple方法,所以控制流會轉而尋找apple類,這是控制流第一次到達該類,因而就會對其中的靜態域進行初始化:
也就是上圖所示的static1,static2,static3三個變量。
初始化調用瞭如下靜態方法,定義該方法的目的就是在控制檯中打印信息。
注意到控制檯中的前兩條輸出:
===============================================================
完成了類的加載,就開始建立具體的對象了,此時控制流開始爲apple1對象建立內存空間,非static域首先被賦值爲0,而後執行對應的初始化語句;
執行完以後,終於進入構造器,執行構造器內的初始化語句;
控制檯產生以下輸出:
而後執行構造其中的剩下語句,將apple1中的各個字段的值打印出來:
其中的static3與value3沒有指定初始值,所以默認爲0;
會到main方法,終於完成了apple1對象的建立,開始執行apple2的建立:
控制檯中的輸出:
最後,終於執行到了main方法的第一條語句,實屬不易:
經過上述例子,能夠對java中的初始化過程作出一個總結:
構造器的構造過程涉及更復雜的知識,本菜雞尚未學到,等後面學習了相應的知識再進行補充;
因爲剛剛接觸java,文章中錯誤很多,求看官大佬指正;