定義:在定義屬性的位置上,在任何方法之外,定義一個代碼塊
種類:分為兩類即動態代碼塊、靜態代碼塊
<1> 動態初始代碼塊:在初始化屬性之前調用初始化代碼塊 {……}
<2> 靜態初始代碼塊:在類載入時運行 static{……} 只被運行一次,往往用作一個類的準備工作
範例程式碼:
package mark.zhang;<br />public class Linux {<br />/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--靜態初始化代碼塊--");<br />}</p><p>/**<br /> * 測試<br /> *<br /> * @param args<br /> */<br />public static void main(String[] args) {<br />Linux linux = new Linux();<br />}<br />}<br />
執行結果:
--靜態初始化代碼塊--<br />--動態初始化代碼塊--
從結果看出,建立Linux類的執行個體時載入這個類,從而代碼塊執行,只是靜態代碼塊先於動態代碼塊之前執行。那麼,我們思考兩個問題,類何時被載入,代碼塊、構造方法以及成員變數之間的執行順序是誰先誰後??汗,別急,往下看。
先說一說類載入時機吧!!
(1)new 一個對象的時候,載入
(2)沒有建立對象,訪問類中靜態方法(final和非final的均可以)和靜態屬性(不可以被final修飾,final修飾的靜態屬性是在常量池裡),載入
(3)聲明一個類的引用,不載入
(4)建立子類,先載入父類,再載入子類
(5)父類中的公開靜態方法,子類繼承,使用子類的類名調用此方法,載入父類
注意,這裡需要滿足兩個條件:
<a> 子類不可以重寫該靜態方法,其實父類的靜態方法子類是不可以重寫只可以繼承的。即使重寫也要加上static關鍵字。那麼也就說明父類的靜態方法是不可以被子類重寫的,子類重寫的這個靜態方法稱之為子類自己的靜態方法(只不過和父類的該靜態方法重名罷了!!)
<b> main方法不可以在子類中,看例子代碼,如下:
package mark.zhang;<br />public class Linux {<br />/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--父類Linux--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--父類Linux--靜態初始化代碼塊--");<br />}</p><p>static void get() {<br />System.out.println("--父類Linux--get()");<br />}<br />}<br />class Ubuntu extends Linux {</p><p>/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--子類Linux--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--子類ubuntu靜態初始化代碼塊--");<br />}<br />}<br />class Test {<br />/**<br /> * 測試<br /> *<br /> * @param args<br /> */<br />public static void main(String[] args) {<br />Ubuntu.get();<br />}<br />}<br />
在主函數中運行以下代碼:
//載入了父類之後,虛擬機器已經知道m()方法的調用了,就不會再載入子類,消極式載入Ubuntu.get();
運行結果:
--父類Linux--靜態初始化代碼塊--<br />--父類Linux--get()
(6)沒有建立對象,訪問類中靜態常量(能計算出結果的常量,在編譯的時候會用計算出來的結果替換運算式),不載入
(7)沒有建立對象,訪問類中靜態常量(不確定的值),載入
關於(6)(7),舉兩個個小例子吧??!!!
package my.test;<br />public class FinalTest {<br />// 編譯時間常量,編譯器在編譯時間會將所有用到該變數的地方替換成相應的字面值<br />public static final int a = 4+4;</p><p>static {<br />System.out.println("--static code--");<br />}<br />}<br />class Test {<br />public static void main(String[] args) {<br />System.out.println("FinalTest.a= " + FinalTest.a);<br />}<br />}
該例子中沒有執行靜態代碼塊,正如(6)所說。
運行結果:
FinalTest.a= 8
再看一個例子,來說明(7)的正確性!!!
package my.test;<br />public class FinalTest {<br />// 運行時常量,在啟動並執行時候才能確定<br />public static final int a = 4 + Math.abs(1);</p><p>static {<br />System.out.println("--static code--");<br />}<br />}<br />class Test {<br />public static void main(String[] args) {<br />System.out.println("FinalTest.a= " + FinalTest.a);<br />}<br />}
運行結果,執行靜態代碼塊內容。
--static code--<br />FinalTest.a= 5
ok,上面說的都是關於類的載入時機問題,接著說說代碼塊、構造方法以及成員變數之間的執行順序的問題。要想說明白這個問題,需要從兩個方面說,一個是單純的一個類,另一個就是一個類與另一個類的繼承。
<1> 單純的一個類
package mark.zhang;<br />public class Linux {<br />// 靜態成員變數<br />public static String name = "Linux OS";<br />// 一般變數<br />public int size = 2;</p><p>public Linux() {<br />System.out.println("--父類Linux--構造方法--");<br />}</p><p>/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--父類Linux--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--父類Linux--靜態初始化代碼塊--");<br />}</p><p>}
測試一下,呵呵!!
class Test {<br />/**<br /> * 測試<br /> *<br /> * @param args<br /> */<br />public static void main(String[] args) {<br />new Linux();<br />}<br />}
測試結果,如下:
--父類Linux--靜態初始化代碼塊--<br />--父類Linux--動態初始化代碼塊--<br />--父類Linux--構造方法--
<2> 具有繼承關係的兩個類
package mark.zhang;<br />public class Linux {<br />// 靜態成員變數<br />public static String name = "Linux OS";<br />// 一般變數<br />public int size = 2;</p><p>public Linux() {<br />System.out.println("--父類Linux--構造方法--");<br />}</p><p>/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--父類Linux--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--父類Linux--靜態初始化代碼塊--");<br />}</p><p>}<br />class Ubuntu extends Linux {</p><p>public Ubuntu() {<br />System.out.println("--子類Ubuntu--構造方法--");<br />}</p><p>/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--子類Ubuntu--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--子類ubuntu靜態初始化代碼塊--");<br />}<br />}<br />class Test {<br />/**<br /> * 測試<br /> *<br /> * @param args<br /> */<br />public static void main(String[] args) {<br />new Ubuntu();<br />}<br />}<br />
執行結果,如下:
--父類Linux--靜態初始化代碼塊--<br />--子類ubuntu靜態初始化代碼塊--<br />--父類Linux--動態初始化代碼塊--<br />--父類Linux--構造方法--<br />--子類Ubuntu--動態初始化代碼塊--<br />--子類Ubuntu--構造方法--<br />
至於,代碼塊與成員變數之間誰先誰後,其實是與它們在類中聲明順序有關的。現在以一個小例子說明問題:
package mark.zhang;<br />public class Linux {//靜態與非靜態成員變數與靜態代碼塊、非靜態代碼塊的執行順序與其聲明順序有關</p><p>RedHat rh1 = new RedHat("非靜態成員變數rh1");<br />static RedHat rh11 = new RedHat("靜態成員變數rh11");</p><p>public Linux() {<br />System.out.println("--父類Linux--構造方法--");<br />}</p><p>/**<br /> * 動態代碼塊<br /> */<br />{<br />System.out.println("--父類Linux--動態初始化代碼塊--");<br />}</p><p>/**<br /> * 靜態代碼塊<br /> */<br />static {<br />System.out.println("--父類Linux--靜態初始化代碼塊--");<br />}</p><p>RedHat rh2 = new RedHat("非靜態成員變數rh2");<br />static RedHat rh22 = new RedHat("靜態成員變數rh22");<br />}<br />class RedHat {<br />static int times = 1;<br />public RedHat(String info) {<br />System.out.println(info + ", the order " + (times++));<br />}<br />}
測試:
class Test {<br />/**<br /> * 測試<br /> *<br /> * @param args<br /> */<br />public static void main(String[] args) {<br />new Linux();<br />}<br />}
運行結果:
靜態成員變數rh11, the order 1<br />--父類Linux--靜態初始化代碼塊--<br />靜態成員變數rh22, the order 2<br />非靜態成員變數rh1, the order 3<br />--父類Linux--動態初始化代碼塊--<br />非靜態成員變數rh2, the order 4<br />--父類Linux--構造方法--
部落格分享:
java類載入全過程(http://my.oschina.net/volador/blog/87194)