java規範之衛語句以及try-with-resource語法

衛語句

如果條件語句極其複雜,就應該將條件語句拆解開,然後逐個檢查,並在條件爲真時立刻從函數中返回,這樣的單獨檢查通常被稱之爲“衛語句”(guard clauses)

摘自《重構—改善既有代碼的設計》

衛語句的效果就是將原來需要仔細閱讀代碼、細心整理邏輯的條件判斷整理成一眼能看透的邏輯關係,效果就像以下:

if(it == 活的){

    if(it == 人){

        if(it != 女人){

            return 不喜歡;

        } else {

            return 喜歡;

        }

    } else {

        return 不喜歡;

    }

} else {

    return 不喜歡;

}

以上代碼無非就是表達 我只喜歡活的女人 的意思,但是代碼一層套一層明顯妨礙了我在第一時間獲取最核心信息的效率,使用衛語句來表達就是:

if (it != 活的){return 不喜歡}

if(it!=人){return 不喜歡}

if(it!=女人){return 不喜歡}

if(it == 女人 && it == 活的) {return 喜歡}

if(其他任何情況){return 不喜歡}

大概就是這麼個東西,當然這些代碼有重複的邏輯判斷在裏面,但是代碼中爲了增強代碼的健壯性,多囉嗦兩行代碼對於初學者來說不是什麼壞事。

try-with-resource語法

一、背景
我們知道,在Java編程過程中,如果打開了外部資源(文件、數據庫連接、網絡連接等),我們必須在這些外部資源使用完畢後,手動關閉它們。因爲外部資源不由JVM管理,無法享用JVM的垃圾回收機制,如果我們不在編程時確保在正確的時機關閉外部資源,就會導致外部資源泄露,緊接着就會出現文件被異常佔用,數據庫連接過多導致連接池溢出等諸多很嚴重的問題。

二、傳統的資源關閉方式
爲了確保外部資源一定要被關閉,通常關閉代碼被寫入finally代碼塊中,當然我們還必須注意到關閉資源時可能拋出的異常,於是變有了下面的經典代碼:

public static void main(String[] args) {
    FileInputStream inputStream = null;
    try {
        inputStream = new FileInputStream(new File("test"));
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }
}

熟悉其他語言的朋友可能會開始吐槽了,在C++中,我們可以把關閉資源的代碼放在析構函數中,在C#中,我們有using代碼塊。這些語法都有一個共同的特性,讓外部資源的關閉行爲與外部資源的句柄對象的生命週期關聯,當外部資源的句柄對象生命週期終結時(例如句柄對象已出作用域),外部資源的關閉行爲將被自動調用。這樣不僅更加符合面向對象的編程理念(將關閉外部資源的行爲內聚在外部資源的句柄對象中),也讓代碼更加簡潔易懂。怎麼到了Java這裏,就找不到自動關閉外部資源的語法特性了呢。

三、JDK7及其之後的資源關閉方式
3.1 try-with-resource語法
確實,在JDK7以前,Java沒有自動關閉外部資源的語法特性,直到JDK7中新增了try-with-resource語法,才實現了這一功能。

那什麼是try-with-resource呢?簡而言之,當一個外部資源的句柄對象(比如FileInputStream對象)實現了AutoCloseable接口,那麼就可以將上面的板式代碼簡化爲如下形式:

public static void main(String[] args) {
    try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

將外部資源的句柄對象的創建放在try關鍵字後面的括號中,當這個try-catch代碼塊執行完畢後,Java會確保外部資源的close方法被調用。代碼是不是瞬間簡潔許多!

3.2 實現原理
try-with-resource並不是JVM虛擬機的新增功能,只是JDK實現了一個語法糖,當你將上面代碼反編譯後會發現,其實對JVM虛擬機而言,它看到的依然是之前的寫法:

public static void main(String[] args) {
    try {
        FileInputStream inputStream = new FileInputStream(new File("test"));
        Throwable var2 = null;

        try {
            System.out.println(inputStream.read());
        } catch (Throwable var12) {
            var2 = var12;
            throw var12;
        } finally {
            if (inputStream != null) {
                if (var2 != null) {
                    try {
                        inputStream.close();
                    } catch (Throwable var11) {
                        var2.addSuppressed(var11);
                    }
                } else {
                    inputStream.close();
                }
            }

        }

    } catch (IOException var14) {
        throw new RuntimeException(var14.getMessage(), var14);
    }
}

3.3 異常抑制
通過反編譯的代碼,大家可能注意到代碼中有一處對異常的特殊處理:

var2.addSuppressed(var11);

這是try-with-resource語法涉及的另外一個知識點,叫做異常抑制。當對外部資源進行處理(例如讀或寫)時,如果遭遇了異常,且在隨後的關閉外部資源過程中,又遭遇了異常,那麼你catch到的將會是對外部資源進行處理時遭遇的異常,關閉資源時遭遇的異常將被“抑制”但不是丟棄,通過異常的getSuppressed方法,可以提取出被抑制的異常。

四、總結
1、當一個外部資源的句柄對象實現了AutoCloseable接口,JDK7中便可以利用try-with-resource語法更優雅的關閉資源,消除板式代碼。

2、try-with-resource時,如果對外部資源的處理和對外部資源的關閉均遭遇了異常,“關閉異常”將被抑制,“處理異常”將被拋出,但“關閉異常”並沒有丟失,而是存放在“處理異常”的被抑制的異常列表中。

參考:https://www.cnblogs.com/itZhy/p/7636615.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章