鍍金池/ 教程/ Java/ 代碼塊
Java 集合細(xì)節(jié)(四):保持 compareTo 和 equals 同步
Iterator
使用序列化實現(xiàn)對象的拷貝
fail-fast 機(jī)制
關(guān)鍵字 final
Vector
HashTable
Java 集合細(xì)節(jié)(一):請為集合指定初始容量
強(qiáng)制類型轉(zhuǎn)換
數(shù)組之一:認(rèn)識 JAVA 數(shù)組
Java 集合細(xì)節(jié)(三):subList 的缺陷
hashCode
ArrayList
數(shù)組之二
List 總結(jié)
LinkedList
Java 提高篇(九)—–實現(xiàn)多重繼承
Java 的四舍五入
關(guān)鍵字 static
理解 Java 的三大特性之多態(tài)
抽象類與接口
集合大家族
異常(二)
Java 集合細(xì)節(jié)(二):asList 的缺陷
Map 總結(jié)
TreeSet
equals() 方法總結(jié)
Java 提高篇(十)—–詳解匿名內(nèi)部類
HashMap
Stack
詳解內(nèi)部類
TreeMap
異常(一)
詳解 Java 定時任務(wù)
HashSet
字符串
理解 Java 的三大特性之繼承
理解 Java 的三大特性之封裝
代碼塊

代碼塊

在編程過程中我們可能會遇到如下這種形式的程序:


    public class Test {
        {
            ////
        }
    }

這種形式的程序段我們將其稱之為代碼塊,所謂代碼塊就是用大括號({})將多行代碼封裝在一起,形成一個獨立的數(shù)據(jù)體,用于實現(xiàn)特定的算法。一般來說代碼塊是不能單獨運行的,它必須要有運行主體。在 Java 中代碼塊主要分為四種:

一、普通代碼塊

普通代碼塊是我們用得最多的也是最普遍的,它就是在方法名后面用{}括起來的代碼段。普通代碼塊是不能夠單獨存在的,它必須要緊跟在方法名后面。同時也必須要使用方法名調(diào)用它。


    public class Test {
        public void test(){
            System.out.println("普通代碼塊");
        }
    }

二、靜態(tài)代碼塊

想到靜態(tài)我們就會想到 static,靜態(tài)代碼塊就是用 static 修飾的用{}括起來的代碼段,它的主要目的就是對靜態(tài)屬性進(jìn)行初始化。


    public class Test {
        static{
            System.out.println("靜態(tài)代碼塊");
        }
    }

三、同步代碼塊

使用 synchronized 關(guān)鍵字修飾,并使用“{}”括起來的代碼片段,它表示同一時間只能有一個線程進(jìn)入到該方法塊中,是一種多線程保護(hù)機(jī)制。

四、 構(gòu)造代碼塊

在類中直接定義沒有任何修飾符、前綴、后綴的代碼塊即為構(gòu)造代碼塊。我們明白一個類必須至少有一個構(gòu)造函數(shù),構(gòu)造函數(shù)在生成對象時被調(diào)用。構(gòu)造代碼塊和構(gòu)造函數(shù)一樣同樣是在生成一個對象時被調(diào)用,那么構(gòu)造代碼在什么時候被調(diào)用?如何調(diào)用的呢?看如下代碼:


    public class Test {
        /**
         * 構(gòu)造代碼
         */
        {
            System.out.println("執(zhí)行構(gòu)造代碼塊...");
        }

        /**
         * 無參構(gòu)造函數(shù)
         */
        public Test(){
            System.out.println("執(zhí)行無參構(gòu)造函數(shù)...");
        }

        /**
         * 有參構(gòu)造函數(shù)
         * @param id  id
         */
        public Test(String id){
            System.out.println("執(zhí)行有參構(gòu)造函數(shù)...");
        }
    }

上面定義了一個非常簡單的類,該類包含無參構(gòu)造函數(shù)、有參構(gòu)造函數(shù)以及構(gòu)造代碼塊,同時在上面也提過代碼塊是沒有獨立運行的能力,他必須要有一個可以承載的載體,那么編譯器會如何來處理構(gòu)造代碼塊呢?編譯器會將代碼塊按照他們的順序(假如有多個代碼塊)插入到所有的構(gòu)造函數(shù)的最前端,這樣就能保證不管調(diào)用哪個構(gòu)造函數(shù)都會執(zhí)行所有的構(gòu)造代碼塊。上面代碼等同于如下形式:


    public class Test {
        /**
         * 無參構(gòu)造函數(shù)
         */
        public Test(){
            System.out.println("執(zhí)行構(gòu)造代碼塊...");
            System.out.println("執(zhí)行無參構(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ù)...");
        }

    }

運行結(jié)果


    public static void main(String[] args) {
            new Test();
            System.out.println("----------------");
            new Test("1");
        }
    ------------
    Output:
    執(zhí)行構(gòu)造代碼塊...
    執(zhí)行無參構(gòu)造函數(shù)...
    ----------------
    執(zhí)行構(gòu)造代碼塊...
    執(zhí)行有參構(gòu)造函數(shù)...

從上面的運行結(jié)果可以看出在 new 一個對象的時候總是先執(zhí)行構(gòu)造代碼,再執(zhí)行構(gòu)造函數(shù),但是有一點需要注意構(gòu)造代碼不是在構(gòu)造函數(shù)之前運行的,它是依托構(gòu)造函數(shù)執(zhí)行的。正是由于構(gòu)造代碼塊有這幾個特性,所以它常用于如下場景:

1、初始化實例變量

如果一個類中存在若干個構(gòu)造函數(shù),這些構(gòu)造函數(shù)都需要對實例變量進(jìn)行初始化,如果我們直接在構(gòu)造函數(shù)中實例化,必定會產(chǎn)生很多重復(fù)代碼,繁瑣和可讀性差。這里我們可以充分利用構(gòu)造代碼塊來實現(xiàn)。這是利用編譯器會將構(gòu)造代碼塊添加到每個構(gòu)造函數(shù)中的特性。

2、初始化實例環(huán)境

一個對象必須在適當(dāng)?shù)膱鼍跋虏拍艽嬖?,如果沒有適當(dāng)?shù)膱鼍?,則就需要在創(chuàng)建對象時創(chuàng)建此場景。我們可以利用構(gòu)造代碼塊來創(chuàng)建此場景,尤其是該場景的創(chuàng)建過程較為復(fù)雜。構(gòu)造代碼會在構(gòu)造函數(shù)之前執(zhí)行。

上面兩個常用場景都充分利用構(gòu)造代碼塊的特性,能夠很好的解決在實例化對象時構(gòu)造函數(shù)比較難解決的問題,利用構(gòu)造代碼不僅可以減少代碼量,同時也是程序的可讀性增強(qiáng)了。特別是當(dāng)一個對象的創(chuàng)建過程比較復(fù)雜,需要實現(xiàn)一些復(fù)雜邏輯,這個時候如果在構(gòu)造函數(shù)中實現(xiàn)邏輯,這是不推薦的,因為我們提倡構(gòu)造函數(shù)要盡可能的簡單易懂,所以我們可以使用構(gòu)造代碼封裝這些邏輯實現(xiàn)部分。

五、 靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造函數(shù)執(zhí)行順序

從詞面上我們就可以看出他們的區(qū)別。靜態(tài)代碼塊,靜態(tài),其作用級別為類,構(gòu)造代碼塊、構(gòu)造函數(shù),構(gòu)造,其作用級別為對象。

1、靜態(tài)代碼塊,它是隨著類的加載而被執(zhí)行,只要類被加載了就會執(zhí)行,而且只會加載一次,主要用于給類進(jìn)行初始化。

2、構(gòu)造代碼塊,每創(chuàng)建一個對象時就會執(zhí)行一次,且優(yōu)先于構(gòu)造函數(shù),主要用于初始化不同對象共性的初始化內(nèi)容和初始化實例環(huán)境。

3、構(gòu)造函數(shù),每創(chuàng)建一個對象時就會執(zhí)行一次。同時構(gòu)造函數(shù)是給特定對象進(jìn)行初始化,而構(gòu)造代碼是給所有對象進(jìn)行初始化,作用區(qū)域不同。

通過上面的分析,他們?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)造代碼塊...");
        }

        /**
         * 無參構(gòu)造函數(shù)
         */
        public Test(){
            System.out.println("執(zhí)行無參構(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í)行無參構(gòu)造函數(shù)...
    ----------------------
    執(zhí)行構(gòu)造代碼塊...
    執(zhí)行有參構(gòu)造函數(shù)...