在編程過(guò)程中我們可能會(huì)遇到如下這種形式的程序:
public class Test {
{
////
}
}
這種形式的程序段我們將其稱之為代碼塊,所謂代碼塊就是用大括號(hào)({})將多行代碼封裝在一起,形成一個(gè)獨(dú)立的數(shù)據(jù)體,用于實(shí)現(xiàn)特定的算法。一般來(lái)說(shuō)代碼塊是不能單獨(dú)運(yùn)行的,它必須要有運(yùn)行主體。在 Java 中代碼塊主要分為四種:
普通代碼塊是我們用得最多的也是最普遍的,它就是在方法名后面用{}括起來(lái)的代碼段。普通代碼塊是不能夠單獨(dú)存在的,它必須要緊跟在方法名后面。同時(shí)也必須要使用方法名調(diào)用它。
public class Test {
public void test(){
System.out.println("普通代碼塊");
}
}
想到靜態(tài)我們就會(huì)想到 static,靜態(tài)代碼塊就是用 static 修飾的用{}括起來(lái)的代碼段,它的主要目的就是對(duì)靜態(tài)屬性進(jìn)行初始化。
public class Test {
static{
System.out.println("靜態(tài)代碼塊");
}
}
使用 synchronized 關(guān)鍵字修飾,并使用“{}”括起來(lái)的代碼片段,它表示同一時(shí)間只能有一個(gè)線程進(jìn)入到該方法塊中,是一種多線程保護(hù)機(jī)制。
在類中直接定義沒(méi)有任何修飾符、前綴、后綴的代碼塊即為構(gòu)造代碼塊。我們明白一個(gè)類必須至少有一個(gè)構(gòu)造函數(shù),構(gòu)造函數(shù)在生成對(duì)象時(shí)被調(diào)用。構(gòu)造代碼塊和構(gòu)造函數(shù)一樣同樣是在生成一個(gè)對(duì)象時(shí)被調(diào)用,那么構(gòu)造代碼在什么時(shí)候被調(diào)用?如何調(diào)用的呢?看如下代碼:
public class Test {
/**
* 構(gòu)造代碼
*/
{
System.out.println("執(zhí)行構(gòu)造代碼塊...");
}
/**
* 無(wú)參構(gòu)造函數(shù)
*/
public Test(){
System.out.println("執(zhí)行無(wú)參構(gòu)造函數(shù)...");
}
/**
* 有參構(gòu)造函數(shù)
* @param id id
*/
public Test(String id){
System.out.println("執(zhí)行有參構(gòu)造函數(shù)...");
}
}
上面定義了一個(gè)非常簡(jiǎn)單的類,該類包含無(wú)參構(gòu)造函數(shù)、有參構(gòu)造函數(shù)以及構(gòu)造代碼塊,同時(shí)在上面也提過(guò)代碼塊是沒(méi)有獨(dú)立運(yùn)行的能力,他必須要有一個(gè)可以承載的載體,那么編譯器會(huì)如何來(lái)處理構(gòu)造代碼塊呢?編譯器會(huì)將代碼塊按照他們的順序(假如有多個(gè)代碼塊)插入到所有的構(gòu)造函數(shù)的最前端,這樣就能保證不管調(diào)用哪個(gè)構(gòu)造函數(shù)都會(huì)執(zhí)行所有的構(gòu)造代碼塊。上面代碼等同于如下形式:
public class Test {
/**
* 無(wú)參構(gòu)造函數(shù)
*/
public Test(){
System.out.println("執(zhí)行構(gòu)造代碼塊...");
System.out.println("執(zhí)行無(wú)參構(gòu)造函數(shù)...");
}
/**
* 有參構(gòu)造函數(shù)
* @param id id
*/
public Test(String id){
System.out.println("執(zhí)行構(gòu)造代碼塊...");
System.out.println("執(zhí)行有參構(gòu)造函數(shù)...");
}
}
運(yùn)行結(jié)果
public static void main(String[] args) {
new Test();
System.out.println("----------------");
new Test("1");
}
------------
Output:
執(zhí)行構(gòu)造代碼塊...
執(zhí)行無(wú)參構(gòu)造函數(shù)...
----------------
執(zhí)行構(gòu)造代碼塊...
執(zhí)行有參構(gòu)造函數(shù)...
從上面的運(yùn)行結(jié)果可以看出在 new 一個(gè)對(duì)象的時(shí)候總是先執(zhí)行構(gòu)造代碼,再執(zhí)行構(gòu)造函數(shù),但是有一點(diǎn)需要注意構(gòu)造代碼不是在構(gòu)造函數(shù)之前運(yùn)行的,它是依托構(gòu)造函數(shù)執(zhí)行的。正是由于構(gòu)造代碼塊有這幾個(gè)特性,所以它常用于如下場(chǎng)景:
1、初始化實(shí)例變量
如果一個(gè)類中存在若干個(gè)構(gòu)造函數(shù),這些構(gòu)造函數(shù)都需要對(duì)實(shí)例變量進(jìn)行初始化,如果我們直接在構(gòu)造函數(shù)中實(shí)例化,必定會(huì)產(chǎn)生很多重復(fù)代碼,繁瑣和可讀性差。這里我們可以充分利用構(gòu)造代碼塊來(lái)實(shí)現(xiàn)。這是利用編譯器會(huì)將構(gòu)造代碼塊添加到每個(gè)構(gòu)造函數(shù)中的特性。
2、初始化實(shí)例環(huán)境
一個(gè)對(duì)象必須在適當(dāng)?shù)膱?chǎng)景下才能存在,如果沒(méi)有適當(dāng)?shù)膱?chǎng)景,則就需要在創(chuàng)建對(duì)象時(shí)創(chuàng)建此場(chǎng)景。我們可以利用構(gòu)造代碼塊來(lái)創(chuàng)建此場(chǎng)景,尤其是該場(chǎng)景的創(chuàng)建過(guò)程較為復(fù)雜。構(gòu)造代碼會(huì)在構(gòu)造函數(shù)之前執(zhí)行。
上面兩個(gè)常用場(chǎng)景都充分利用構(gòu)造代碼塊的特性,能夠很好的解決在實(shí)例化對(duì)象時(shí)構(gòu)造函數(shù)比較難解決的問(wèn)題,利用構(gòu)造代碼不僅可以減少代碼量,同時(shí)也是程序的可讀性增強(qiáng)了。特別是當(dāng)一個(gè)對(duì)象的創(chuàng)建過(guò)程比較復(fù)雜,需要實(shí)現(xiàn)一些復(fù)雜邏輯,這個(gè)時(shí)候如果在構(gòu)造函數(shù)中實(shí)現(xiàn)邏輯,這是不推薦的,因?yàn)槲覀兲岢珮?gòu)造函數(shù)要盡可能的簡(jiǎn)單易懂,所以我們可以使用構(gòu)造代碼封裝這些邏輯實(shí)現(xiàn)部分。
從詞面上我們就可以看出他們的區(qū)別。靜態(tài)代碼塊,靜態(tài),其作用級(jí)別為類,構(gòu)造代碼塊、構(gòu)造函數(shù),構(gòu)造,其作用級(jí)別為對(duì)象。
1、靜態(tài)代碼塊,它是隨著類的加載而被執(zhí)行,只要類被加載了就會(huì)執(zhí)行,而且只會(huì)加載一次,主要用于給類進(jìn)行初始化。
2、構(gòu)造代碼塊,每創(chuàng)建一個(gè)對(duì)象時(shí)就會(huì)執(zhí)行一次,且優(yōu)先于構(gòu)造函數(shù),主要用于初始化不同對(duì)象共性的初始化內(nèi)容和初始化實(shí)例環(huán)境。
3、構(gòu)造函數(shù),每創(chuàng)建一個(gè)對(duì)象時(shí)就會(huì)執(zhí)行一次。同時(shí)構(gòu)造函數(shù)是給特定對(duì)象進(jìn)行初始化,而構(gòu)造代碼是給所有對(duì)象進(jìn)行初始化,作用區(qū)域不同。
通過(guò)上面的分析,他們?nèi)叩膱?zhí)行順序應(yīng)該為:靜態(tài)代碼塊 > 構(gòu)造代碼塊 > 構(gòu)造函數(shù)。
public class Test {
/**
* 靜態(tài)代碼塊
*/
static{
System.out.println("執(zhí)行靜態(tài)代碼塊...");
}
/**
* 構(gòu)造代碼塊
*/
{
System.out.println("執(zhí)行構(gòu)造代碼塊...");
}
/**
* 無(wú)參構(gòu)造函數(shù)
*/
public Test(){
System.out.println("執(zhí)行無(wú)參構(gòu)造函數(shù)...");
}
/**
* 有參構(gòu)造函數(shù)
* @param id
*/
public Test(String id){
System.out.println("執(zhí)行有參構(gòu)造函數(shù)...");
}
public static void main(String[] args) {
System.out.println("----------------------");
new Test();
System.out.println("----------------------");
new Test("1");
}
}
-----------
Output:
執(zhí)行靜態(tài)代碼塊...
----------------------
執(zhí)行構(gòu)造代碼塊...
執(zhí)行無(wú)參構(gòu)造函數(shù)...
----------------------
執(zhí)行構(gòu)造代碼塊...
執(zhí)行有參構(gòu)造函數(shù)...