Java如何優(yōu)雅地關(guān)閉資源try-with-resource及其異常抑制
一、背景
我們知道,在Java編程過程中,如果打開了外部資源(文件、數(shù)據(jù)庫連接、網(wǎng)絡(luò)連接等),我們必須在這些外部資源使用完畢后,手動關(guān)閉它們。因為外部資源不由JVM管理,無法享用JVM的垃圾回收機(jī)制,如果我們不在編程時確保在正確的時機(jī)關(guān)閉外部資源,就會導(dǎo)致外部資源泄露,緊接著就會出現(xiàn)文件被異常占用,數(shù)據(jù)庫連接過多導(dǎo)致連接池溢出等諸多很嚴(yán)重的問題。
二、傳統(tǒng)的資源關(guān)閉方式
為了確保外部資源一定要被關(guān)閉,通常關(guān)閉代碼被寫入finally代碼塊中,當(dāng)然我們還必須注意到關(guān)閉資源時可能拋出的異常,于是變有了下面的經(jīng)典代碼:
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++中,我們可以把關(guān)閉資源的代碼放在析構(gòu)函數(shù)中,在C#中,我們有using代碼塊。這些語法都有一個共同的特性,讓外部資源的關(guān)閉行為與外部資源的句柄對象的生命周期關(guān)聯(lián),當(dāng)外部資源的句柄對象生命周期終結(jié)時(例如句柄對象已出作用域),外部資源的關(guān)閉行為將被自動調(diào)用。這樣不僅更加符合面向?qū)ο蟮木幊汤砟睿▽㈥P(guān)閉外部資源的行為內(nèi)聚在外部資源的句柄對象中),也讓代碼更加簡潔易懂。怎么到了Java這里,就找不到自動關(guān)閉外部資源的語法特性了呢。
三、JDK7及其之后的資源關(guān)閉方式
3.1 try-with-resource語法
確實,在JDK7以前,Java沒有自動關(guān)閉外部資源的語法特性,直到JDK7中新增了try-with-resource語法,才實現(xiàn)了這一功能。
那什么是try-with-resource呢?簡而言之,當(dāng)一個外部資源的句柄對象(比如FileInputStream對象)實現(xiàn)了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);
}
}
將外部資源的句柄對象的創(chuàng)建放在try關(guān)鍵字后面的括號中,當(dāng)這個try-catch代碼塊執(zhí)行完畢后,Java會確保外部資源的close方法被調(diào)用。代碼是不是瞬間簡潔許多!
3.2 實現(xiàn)原理
try-with-resource并不是JVM虛擬機(jī)的新增功能,只是JDK實現(xiàn)了一個語法糖,當(dāng)你將上面代碼反編譯后會發(fā)現(xiàn),其實對JVM虛擬機(jī)而言,它看到的依然是之前的寫法:
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語法涉及的另外一個知識點,叫做異常抑制。當(dāng)對外部資源進(jìn)行處理(例如讀或?qū)懀r,如果遭遇了異常,且在隨后的關(guān)閉外部資源過程中,又遭遇了異常,那么你catch到的將會是對外部資源進(jìn)行處理時遭遇的異常,關(guān)閉資源時遭遇的異常將被“抑制”但不是丟棄,通過異常的getSuppressed方法,可以提取出被抑制的異常。
四、總結(jié)
1、當(dāng)一個外部資源的句柄對象實現(xiàn)了AutoCloseable接口,JDK7中便可以利用try-with-resource語法更優(yōu)雅的關(guān)閉資源,消除板式代碼。
2、try-with-resource時,如果對外部資源的處理和對外部資源的關(guān)閉均遭遇了異常,“關(guān)閉異?!睂⒈灰种?,“處理異?!睂⒈粧伋?,但“關(guān)閉異常”并沒有丟失,而是存放在“處理異常”的被抑制的異常列表中。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用java基于pushlet和bootstrap實現(xiàn)的簡單聊天室
這篇文章主要介紹了使用java基于pushlet和bootstrap實現(xiàn)的簡單聊天室的相關(guān)資料,需要的朋友可以參考下2015-03-03
Java的Struts框架中<results>標(biāo)簽的使用方法
這篇文章主要介紹了Java的Struts框架中<results>標(biāo)簽的使用方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11
使用JVMTI實現(xiàn)SpringBoot的jar加密,防止反編譯
這篇文章主要介紹了使用JVMTI實現(xiàn)SpringBoot的jar加密,防止反編譯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Mybatis?Interceptor線程安全引發(fā)的bug問題
這篇文章主要介紹了Mybatis?Interceptor線程安全引發(fā)的bug問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
Java和MySQL數(shù)據(jù)庫中關(guān)于小數(shù)的保存問題詳析
在Java和MySQL中小數(shù)的精度可能會受到限制,如float類型的小數(shù)只能精確到6-7位,double類型也只能精確到15-16位,這篇文章主要給大家介紹了關(guān)于Java和MySQL數(shù)據(jù)庫中關(guān)于小數(shù)的保存問題,需要的朋友可以參考下2024-01-01
SpringBoot SSO輕松實現(xiàn)(附demo)
這篇文章主要介紹了SpringBoot SSO輕松實現(xiàn)(附demo),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

