Java程式碼塊與程式碼載入順序原理詳解
這篇文章主要介紹了Java程式碼塊與程式碼載入順序原理詳解,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
本文首先介紹幾個基本的名次,然後介紹了三種程式碼塊的特性和使用方法。
在面試大型公司時,如果遇到大型國企或者大的網際網路私企,筆試中經常遇到程式碼塊和程式碼載入順序的筆試題。這裡做一個總結,也方便各位小夥伴飆車不會飄。
名詞解釋
程式碼塊
由 { } 包起來的程式碼,稱為程式碼塊
靜態程式碼塊
由 static { } 包起來的程式碼,稱為靜態程式碼塊。
不同型別變數定義示例:
class Demo{ String x;// 非靜態成員變數,又稱為屬性,對該類不同的物件來說,屬性互不相同 static int y = 32;// 類變數,一個類中只有一個該變數,該類不同的物件共享同一個靜態成員變數 public static void main(String[] args){ int z = 0;// 區域性變數,只在方法內部可見,在方法結束後由垃圾收集器自動回收 } }
區域性程式碼塊
位置:區域性位置(方法內部)。
作用:限定變數的生命週期,儘早釋放,節約記憶體。
呼叫:呼叫其所在的方法時執行。
方法中的區域性程式碼塊一般進行一次性地呼叫,呼叫完立刻釋放空間,避免在接下來的呼叫過程中佔用棧空間。棧空間記憶體有限,方法呼叫可能會生成很多區域性變數導致棧記憶體不足,使用區域性程式碼塊可以避免此缺陷。
public class 區域性程式碼塊 { public static void go() { // 區域性程式碼塊 { int age = 30; System.out.print("go: " + age); } } public static void main(String[] args) { go(); } }
構造程式碼塊
位置:類成員的位置,即類中方法之外的位置。
作用:把多個構造方法共同的部分提取出來,共用構造程式碼塊。
呼叫:每次呼叫構造方法時,都會優先於構造方法執行,也就是每次new一個物件時自動呼叫,實現物件初始化。
public class A { int i = 1; int initValue;//成員變數,初始化交給程式碼塊來完成 A(){ System.out.println("構造方法在程式碼塊執行後執行"); } { System.out.println("程式碼塊從上至下依次執行"); //程式碼塊的作用體現於此:在呼叫構造方法之前,用某段程式碼對成員變數進行初始化。 //而不是在構造方法呼叫時再進行。 for (int i = 0;i < 100;i ++) { initValue += i; } } { System.out.println(initValue); System.out.println(i);//此時會列印1 int i = 2;//區域性變數,和成員變數不衝突,但會優先使用程式碼塊的變數 System.out.println(i);//此時列印2 //System.out.println(j);//提示非法向後引用,因為此時j的的初始化還沒開始。 } int j = 2; { System.out.println(j); System.out.println(i);//程式碼塊中的變數執行後自動釋放,不會影響程式碼塊之外的程式碼 } } public class 構造程式碼塊 { @Test public void test() { A a = new A(); } }
執行結果
程式碼塊從上至下依次執行 1 2 構造方法在程式碼塊執行後執行
靜態程式碼塊
位置:類成員位置。
作用:對類進行一些初始化,只加載一次。當new多個物件時,只有第一次會呼叫靜態程式碼塊,因為靜態程式碼塊和類變數一樣,
是屬於類的,所有物件共享一份。
呼叫: new 一個物件時自動呼叫。
public class 靜態程式碼塊 { @Test public void test() { C c1 = new C(); C c2 = new C(); //結果,靜態程式碼塊只會呼叫一次,類的所有物件共享該程式碼塊 System.out.println("我是普通方法"); } } class C{ C(){ System.out.println("構造方法呼叫"); } { System.out.println("程式碼塊呼叫"); } static { System.out.println("靜態程式碼塊呼叫"); } }
呼叫結果:
靜態程式碼塊呼叫 程式碼塊呼叫 構造方法呼叫 程式碼塊呼叫 構造方法呼叫 我是普通方法
執行順序 靜態程式碼塊 —–> 構造程式碼塊 ——-> 構造方法
筆試題
寫出下列程式輸出結果:
public class HelloA { public HelloA(){ System.out.println("HelloA"); } { System.out.println("I'm A class"); } static { System.out.println("static A"); } } public class HelloB extends HelloA { public HelloB(){ System.out.println("HelloB"); } { System.out.println("I'm B class"); } static { System.out.println("static B"); } public static void main(String[] args) { new HelloB(); } }
執行結果:
分析:首先要知道靜態程式碼塊是隨著類的載入而載入,而構造程式碼塊和構造方法都是隨著物件的建立而載入。
1,在編譯HelloB.java時,由於HelloB 繼承 HelloA,先載入了HelloA類,因此HelloA類的靜態程式碼塊首先執行,而後載入HelloB類,HelloB類的靜態程式碼塊執行,這沒什麼好說的。
2,然後建立HelloB的物件,大家都知道構造程式碼塊優先於構造方法執行,這時候問題來了,這時應該先看HelloB類的構造方法,HelloB類裡的構造方法裡有一句隱式的super()首先被執行,所以找到HelloA類的構造方法,而HelloA類的構造方法中也有一句隱式的super()執行(呼叫Object類的構造方法),並沒有什麼返回結果,接下來才是在執行HelloA類構造方法的方法體前先執行了HelloA類的構造程式碼塊(I'm A class),再執行HelloA類構造方法的方法體(也就是Hello A),最後又回到HelloB類的構造方法中,這時HelloB類的super()已經執行完了,在執行HelloB類構造方法的方法體前先執行HelloB類的構造程式碼塊(I'm B class),再執行子類構造方法的方法體(HellB)。
無繼承初始化順序:
有繼承初始化順序:
接下來看一道阿里筆試題:
public class B{ public static B t1 = new B(); public static B t2 = new B(); { System.out.println("構造塊"); } static { System.out.println("靜態塊"); } public static void main(String[] args) { B t =new B(); } }
執行結果:
總結
Java程式碼初始化順序
由 static 關鍵字修飾的,如類變數和靜態程式碼塊,將在類建立例項之前被初始化,而且是按順序從上到下依次被執行。(類變數、靜態程式碼塊)屬於類本身,不依賴於類的例項。
沒有 static 關鍵字修飾的(如:例項變數(非靜態變數)、非靜態程式碼塊)初始化實際上是會被提取到類的構造器中被執行的,但是會比類構造器中的程式碼塊優先執行。例項變數、非靜態程式碼塊的地位是相等的,它們將按順序被執行。
容易混淆的一個知識點
靜態方法只允許直接訪問靜態成員,而例項方法中可以訪問靜態成員和例項成員,原因是類還沒有例項化,所以例項成員也沒有被建立,靜態方法中因此也不能用this。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。