Effective Java 3rd 條目9 try-with-resources優(yōu)于try-finally

Java庫包含很多資源厕诡,這些資源必須手動調(diào)用close方法關(guān)閉蝗锥。這樣的例子包括InputStream、OutputStream,和java.sql.Connection此迅。關(guān)閉資源常常被客戶端忽略铁孵,可以預見會有可怕的性能后果锭硼。許多這些資源用finalizer作為安全保障,但是finalizer不會很好的工作(條目8)蜕劝。

歷史上檀头,一個資源應該正確地關(guān)閉,即使面對一個異澄跤睿或者返回鳖擒,try-finally語句是最好的保證方式:

// try-finally - 不再是關(guān)閉資源最好的方式!
static String firstLineOfFile(String path) throws IOException { 
    BufferedReader br = new BufferedReader(new FileReader(path)); 
    try { 
        return br.readLine(); 
    } finally { 
        br.close(); 
    } 
}

這可能看上去不太糟,但是當你添加第二個資源時會變得更糟:

// 當用于多于一個資源時烫止,try-finally 是丑陋的! 
static void copy(String src, String dst) throws IOException {       
    try { 
        InputStream in = new FileInputStream(src); 
        try { 
            OutputStream out = new FileOutputStream(dst); 
            byte[] buf = new byte[BUFFER_SIZE]; 
            int n; 
            while ((n = in.read(buf)) >= 0) 
                out.write(buf, 0, n); 
        } finally { 
            out.close(); 
        } 
    } finally { 
        in.close(); 
    }
}

這可能難于置信蒋荚,但是優(yōu)秀的程序員甚至多數(shù)時間會把這個弄錯。一開始馆蠕,我在Java Puzzlers [Bloch05] 88頁把這個弄錯了期升,而且?guī)啄隂]人注意到惊奇。事實上在2007年,Java庫的close方法的使用播赁,三分之二是錯誤的颂郎。

用try-finally語句關(guān)閉資源的正確代碼,就像前面兩個代碼例子容为,甚至有微妙的缺陷乓序。在兩個try代碼塊和finally代碼塊中的代碼可能會拋出異常。比如坎背,firstLineOfFile方法中替劈,由于底層物體設備的失敗,調(diào)用readLine可能拋出一個異常得滤,而且close方法調(diào)用可能因為同樣的原因失敗陨献。在這些情形下,第二個異常完全掩蓋了第一個懂更。在異常棧信息中沒有第一個異常的記錄眨业,在實際系統(tǒng)中這個可能使得調(diào)試非常復雜,通常為了診斷這個問題沮协,第一個異常是我們想要看見的龄捡。為了第一個異常而抑制第二個,雖然編寫這樣的代碼是可能的皂股,事實上墅茉,沒有人這么干,因為這太啰嗦了呜呐。

當Java 7引進了try-with-resource語句[JLS, 14.20.3],這些問題全解決了悍募。為了用在這個結(jié)構(gòu)蘑辑,一個資源必須實現(xiàn)AutoCloseable接口,這個接口包含一個返回空的close方法坠宴。Java庫和第三方庫中的許多類和接口現(xiàn)在已經(jīng)實現(xiàn)或者擴展了AutoCloseable洋魂。如果你編寫一個必須關(guān)閉資源的類,你的類也應該實現(xiàn)AutoCloseable喜鼓。

下面是我們第一個例子使用try-with-resource的樣子:

// try-with-resources - 關(guān)閉資源的最好方式副砍!
static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader( 
            new FileReader(path))) {
        return br.readLine();
    } 
}

下面是我們第二個例子使用try-with-resource的樣子:

// 多個資源的try-with-resources - 簡短而明了
static void copy(String src, String dst) throws IOException { 
    try (InputStream in = new FileInputStream(src);
            OutputStream out = new FileOutputStream(dst)) { 
        byte[] buf = new byte[BUFFER_SIZE]; 
        while ((n = in.read(buf)) >= 0) 
            out.write(buf, 0, n); 
        }
}

不僅try-with-resource版本比原來更加簡短和可讀,而且它們提供了好得多的可診斷性庄岖』眙幔考慮firstLineOfFile方法。如果異常由兩個readLine調(diào)用和(不可見的)close方法拋出隅忿,為了前面的異常心剥,后面的抑制了邦尊。事實上,為了保護你真實想看到的異常优烧,多個異巢踝幔可以被抑制。這些抑制的異常沒有被丟棄畦娄,它們打印在在一個堆棧信息里面又沾,用一個注釋說明它們被抑制了。你也可以用getSuppressed方法以編程方式獲取它們熙卡,在Java 7中這個方法添加到了Throwable杖刷。

你可以把catch子句放在try-with-resource語句,就像你可以放在try-finally語句一樣再膳。這讓你可以處理異常挺勿,而沒有用另外一層嵌套來污染你的代碼。作為有點特別的例子喂柒,下面是firstLineOfFile方法的一個版本不瓶,它不拋出異常,但是如果不能夠打開或者讀取文件灾杰,它返回一個默認值:

// 有catch子句的try-with-resource 
static String firstLineOfFile(String path, String defaultVal) { 
    try (BufferedReader br = new BufferedReader( 
            new FileReader(path))) { 
        return br.readLine(); 
    } catch (IOException e) { 
        return defaultVal; 
    } 
}

這堂課是很清晰的:當涉及必須關(guān)閉的資源的時候蚊丐,總是優(yōu)先使用try-with-resource,而不是try-finally艳吠。這樣的代碼更加簡短和清晰麦备,而且它產(chǎn)生的異常也更加有用。對于使用必須關(guān)閉的資源的代碼昭娩,try-with-resource語句使得正確編寫這些代碼更容易凛篙,而用try-finally是在實踐中不可能的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栏渺,一起剝皮案震驚了整個濱河市呛梆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磕诊,老刑警劉巖填物,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異霎终,居然都是意外死亡滞磺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門莱褒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來击困,“玉大人,你說我怎么就攤上這事保礼∨胬” “怎么了责语?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長目派。 經(jīng)常有香客問我坤候,道長,這世上最難降的妖魔是什么企蹭? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任白筹,我火速辦了婚禮,結(jié)果婚禮上谅摄,老公的妹妹穿的比我還像新娘徒河。我一直安慰自己,他們只是感情好送漠,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布顽照。 她就那樣靜靜地躺著,像睡著了一般闽寡。 火紅的嫁衣襯著肌膚如雪代兵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天爷狈,我揣著相機與錄音植影,去河邊找鬼。 笑死涎永,一個胖子當著我的面吹牛思币,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羡微,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼谷饿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妈倔?” 一聲冷哼從身側(cè)響起各墨,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎启涯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恃轩,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡结洼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叉跛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片松忍。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筷厘,靈堂內(nèi)的尸體忽然破棺而出鸣峭,到底是詐尸還是另有隱情宏所,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布摊溶,位于F島的核電站爬骤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏莫换。R本人自食惡果不足惜霞玄,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拉岁。 院中可真熱鬧坷剧,春花似錦、人聲如沸喊暖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陵叽。三九已至狞尔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咨跌,已是汗流浹背沪么。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锌半,地道東北人禽车。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像刊殉,于是被迫代替她去往敵國和親殉摔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎部分 基礎部分的順序:基本語法记焊,類相關(guān)的語法逸月,內(nèi)部類的語法,繼承相關(guān)的語法遍膜,異常的語法碗硬,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 此心光明,亦復何言 Try-with-resources Try-with-resources是Java7出現(xiàn)的一...
    hongXkeX閱讀 1,550評論 0 2
  • 1. 簡介 1.1 什么是 MyBatis 瓢颅? MyBatis 是支持定制化 SQL恩尾、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,429評論 0 4
  • 文這才明白,這么多年積攢在硬盤里的電影挽懦,都是在等著mo一起來看翰意。時間終將告訴人們,你所做的一切都會是有意義的,只要...
    wen_a閱讀 395評論 1 1
  • 今天是傳統(tǒng)的女性節(jié)日。以前叫婦女節(jié)进鸠,現(xiàn)在改為女神節(jié)稠曼、女王節(jié)。不管怎么說堤如,都是我們在過節(jié)蒲列。 自從2012年有了你...
    曉曉豐臨閱讀 188評論 0 0