讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議36:使用構造代碼塊精煉程序 >

建議36:使用構造代碼塊精煉程序

什麼叫代碼塊(Code Block)?用大括號把多行代碼封裝在一起,形成一個獨立的數據體,實現特定算法的代碼集合即為代碼塊,一般來說代碼塊是不能單獨運行的,必須要有運行主體。在Java中一共有四種類型的代碼塊:

(1)普通代碼塊

就是在方法後面使用「{}」括起來的代碼片段,它不能單獨執行,必須通過方法名調用執行。

(2)靜態代碼塊

在類中使用static修飾,並使用「{}」括起來的代碼片段,用於靜態變量的初始化或對像創建前的環境初始化。

(3)同步代碼塊

使用synchronized關鍵字修飾,並使用「{}」括起來的代碼片段,它表示同一時間只能有一個線程進入到該方法塊中,是一種多線程保護機制。

(4)構造代碼塊

在類中沒有任何的前綴或後綴,並使用「{}」括起來的代碼片段。

我們知道,一個類至少有一個構造函數(如果沒有,編譯器會無私地為其創建一個無參構造函數),構造函數是在對像生成時調用的,那現在的問題來了:構造函數和構造代碼塊是什麼關係?構造代碼塊是在什麼時候執行的?在回答這個問題之前,我們先來看看編譯器是如何處理構造代碼塊的,看如下代碼:


public class Client{

{

//構造代碼塊

System.out.println("執行構造代碼塊");

}

public Client(){

System.out.println("執行無參構造");

}

public Client(String_str){

System.out.println("執行有參構造");

}

}


這是一段非常簡單的代碼,它包含了構造代碼塊、無參構造、有參構造,我們知道代碼塊不具有獨立執行的能力,那麼編譯器是如何處理構造代碼塊呢?很簡單,編譯器會把構造代碼塊插入到每個構造函數的最前端,上面的代碼與如下代碼等價:


public class Client{

public Client(){

System.out.println("執行構造代碼塊");

System.out.println("執行無參構造");

}

public Client(String_str){

System.out.println("執行構造代碼塊");

System.out.println("執行有參構造");

}

}


每個構造函數的最前端都被插入了構造代碼塊,很顯然,在通過new關鍵字生成一個實例時會先執行構造代碼塊,然後再執行其他代碼,也就是說:構造代碼塊會在每個構造函數內首先執行(需要注意的是:構造代碼塊不是在構造函數之前運行的,它依托於構造函數的執行),明白了這一點,我們就可以把構造代碼塊應用到如下場景中:

(1)初始化實例變量(Instance Variable)

如果每個構造函數都要初始化變量,可以通過構造代碼塊來實現。當然也可以通過定義一個方法,然後在每個構造函數中調用該方法來實現,沒錯,可以解決,但是要在每個構造函數中都調用該方法,而這就是其缺點,若採用構造代碼塊的方式則不用定義和調用,會直接由編譯器寫入到每個構造函數中,這才是解決此類問題的絕佳方式。

(2)初始化實例環境

一個對像必須在適當的場景下才能存在,如果沒有適當的場景,則就需要在創建對像時創建此場景,例如在JEE開發中,要產生HTTP Request必須首先建立HTTP Session,在創建HTTP Request時就可以通過構造代碼塊來檢查HTTP Session是否已經存在,不存在則創建之。

以上兩個場景利用了構造代碼塊的兩個特性:在每個構造函數中都運行和在構造函數中它會首先運行。很好地利用構造代碼塊的這兩個特性不僅可以減少代碼量,還可以讓程序更容易閱讀,特別是當所有的構造函數都要實現邏輯,而且這部分邏輯又很複雜時,這時就可以通過編寫多個構造代碼塊來實現。每個代碼塊完成不同的業務邏輯(當然了,構造函數盡量簡單,這是基本原則),按照業務順序依次存放,這樣在創建實例對像時JVM也就會按照順序依次執行,實現複雜對象的模塊化創建。