鍍金池/ 教程/ Java/ 代碼塊
Java 集合細(xì)節(jié)(四):保持 compareTo 和 equals 同步
Iterator
使用序列化實(shí)現(xiàn)對(duì)象的拷貝
fail-fast 機(jī)制
關(guān)鍵字 final
Vector
HashTable
Java 集合細(xì)節(jié)(一):請(qǐng)為集合指定初始容量
強(qiáng)制類型轉(zhuǎn)換
數(shù)組之一:認(rèn)識(shí) JAVA 數(shù)組
Java 集合細(xì)節(jié)(三):subList 的缺陷
hashCode
ArrayList
數(shù)組之二
List 總結(jié)
LinkedList
Java 提高篇(九)—–實(shí)現(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 定時(shí)任務(wù)
HashSet
字符串
理解 Java 的三大特性之繼承
理解 Java 的三大特性之封裝
代碼塊

代碼塊

在編程過(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)代碼塊

想到靜態(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ī)制。

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

在類中直接定義沒(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)部分。

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

從詞面上我們就可以看出他們的區(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ù)...