Java 代碼字節(jié):足智多謀的 Try-With-Resources

當(dāng)通過(guò) Java 實(shí)現(xiàn)業(yè)務(wù)實(shí)例時(shí),對(duì)資源進(jìn)行處理是司空見(jiàn)慣的。一般情況下,資源(如文件或 socket 句柄)封裝在對(duì)象中,使用后必須關(guān)閉才能釋放資源。通常開(kāi)發(fā)人員有責(zé)任關(guān)閉自己所創(chuàng)建的資源,以避免資源沖突,一般都會(huì)放在 finally 語(yǔ)句塊中處理。不這樣做其實(shí)也不會(huì)產(chǎn)生編譯錯(cuò)誤,但很容易導(dǎo)致資源泄露。雖然現(xiàn)在靜態(tài)代碼檢查工具足夠聰明,也可以做出提示。但不是每個(gè)人都使用工具,而且這些警告也容易被忽略。

Java 7 中首次引入了一種新的處理(關(guān)閉)資源的方式——try-with-resources。它使得在 try-catch 語(yǔ)句塊中的資源能按照正確順序自動(dòng)關(guān)閉,更加容易地處理資源。

我們來(lái)一起看一個(gè)業(yè)務(wù)實(shí)例的實(shí)現(xiàn),其需要從數(shù)據(jù)庫(kù)中獲取指定賬戶的狀態(tài)碼。首先可以看到它是如何以傳統(tǒng)方式實(shí)現(xiàn),緊接著是足智多謀的 try-with-resources 如何實(shí)現(xiàn)。最后,還將看到 Java 9 引入的更加簡(jiǎn)潔的版本。

傳統(tǒng)的方式處理資源(Java 7 之前)

// 代碼已簡(jiǎn)化,只保留跟眼下話題相關(guān)的內(nèi)容。

public static int getAccountStatusCodeFromDataStore_traditional(String accountId) throws SQLException {

String accountStatusCodeQuery = getAccountStatusCodeQuery(accountId);

Statement statement = null;

ResultSet resultSet = null;

try {

statement = createStatementFromConnection();

resultSet = statement.executeQuery(accountStatusCodeQuery);

return getAccountStatusCodeFromResultSet(resultSet);

} finally {

if (resultSet != null)

resultSet.close();

if (statement != null)

statement.close();

}

}

如上所示,我們必須增加 finally 語(yǔ)句塊來(lái)處理資源關(guān)閉。在調(diào)用 close 方法之前,須顯示地檢查 null 值,并且同時(shí)要保證關(guān)閉資源的邏輯順序。代碼不但變得冗長(zhǎng),而且我們?cè)?jīng)遇到過(guò)許多開(kāi)發(fā)人員會(huì)忘記編寫(xiě) finally 語(yǔ)句塊來(lái)關(guān)閉資源,導(dǎo)致資源泄露的情況。

順便提一下,假如 try 和 finally 語(yǔ)句塊都拋出異常,finally 語(yǔ)句塊拋出的異常會(huì)屏蔽對(duì)方。

Java 7/8 中通過(guò) try-with-resources 處理資源

現(xiàn)通過(guò) try-with-resources 實(shí)現(xiàn)與上面相同的代碼塊,如下所示:

// 代碼已簡(jiǎn)化,只保留跟眼下話題相關(guān)的內(nèi)容。

public static int getAccountStatusCodeFromDataStore_tryWithResourcesJava7(String accountId) throws SQLException {

String accountStatusCodeQuery = getAccountStatusCodeQuery(accountId);

try (Statement statement = createStatementFromConnection();

ResultSet resultSet = statement.executeQuery(accountStatusCodeQuery)) {

return getAccountStatusCodeFromResultSet(resultSet);

}

}

在本例中可以看到簡(jiǎn)潔的代碼有助于提高整體可讀性,資源管理也自動(dòng)完成。try-with-resources 語(yǔ)句中可以包含多個(gè)資源,它們之間應(yīng)通過(guò)分號(hào)隔開(kāi)。資源會(huì)在保持邏輯順序的前提下自動(dòng)關(guān)閉(最后聲明的將第一個(gè)關(guān)閉)。

如果在 try-with-resources 和 try 語(yǔ)句塊中拋出異常,從 try 中拋出的異常將會(huì)屏蔽對(duì)方。假如有需要,可從 try 語(yǔ)句塊拋出的異常中,通過(guò)調(diào)用 Throwable.getSuppressed 方法找回屏蔽的異常。

try-with-resources 語(yǔ)句中也可以寫(xiě) catch 和 finally 語(yǔ)句塊。任何 catch 和 finally 語(yǔ)句塊會(huì)在聲明的資源關(guān)閉后運(yùn)行。

Java 9 中通過(guò) try-with-resources 處理資源

Java 9 中引入了更加簡(jiǎn)練的版本。如果已經(jīng)把資源聲明為 final 或 effective final,則在 try-with-resources 中無(wú)需創(chuàng)建任何新的變量,可直接使用。這使得能夠利用自動(dòng)資源管理?,F(xiàn)通過(guò)更簡(jiǎn)潔的 try-with-resources 語(yǔ)句來(lái)實(shí)現(xiàn)與上面相同的代碼塊,如下所示:

// 代碼已簡(jiǎn)化,只保留跟眼下話題相關(guān)的內(nèi)容。

public static int getAccountStatusCodeFromDataStore_tryWithResourcesJava9(String accountId) throws SQLException {

String accountStatusCodeQuery = getAccountStatusCodeQuery(accountId);

// 顯示地聲明 final

final Statement statement = createStatementFromConnection();

// effective final

ResultSet resultSet = statement.executeQuery(accountStatusCodeQuery);

try (statement; resultSet) {

return getAccountStatusCodeFromResultSet(resultSet);

}

}

幕后如何運(yùn)行

Java 7 引入了專門設(shè)計(jì)用于 try-with-resources 語(yǔ)句的 AutoCloseable 接口。Java 5 引入的 Closeable 接口也修改為繼承 AutoCloseable 接口。這兩個(gè)接口都擁有抽象的 close 方法,資源應(yīng)該實(shí)現(xiàn)它并提供有效的方法。任何實(shí)現(xiàn) AutoCloseable 和 Closeable 接口的資源都可以通過(guò) try-with-resources 來(lái)關(guān)閉。所有基于 JDK 資源的類和接口都已修改成繼承這兩個(gè)接口其中之一,使之能與現(xiàn)有的 try-with-resources 語(yǔ)句兼容。

然而,若處理的資源沒(méi)有實(shí)現(xiàn) AutoCloseable 或 Closeable 接口,則必須使用傳統(tǒng)的方法來(lái)關(guān)閉。

關(guān)鍵要點(diǎn)

try-with-resources 有助于自動(dòng)資源管理,不需要編寫(xiě)顯示的 finally 語(yǔ)句塊來(lái)處理關(guān)閉資源。下面是對(duì) try-with-resources 關(guān)鍵點(diǎn)的總結(jié):

有助于實(shí)現(xiàn)簡(jiǎn)練清晰的代碼。

可以在 try-with-resources 語(yǔ)句中同時(shí)處理多個(gè)資源。

在 Java 7/8 ,try-with-resources 語(yǔ)句中必須聲明要關(guān)閉的資源。通過(guò)這種方式聲明的資源屬于隱式 final。

Java 9 中甚至能使用預(yù)先創(chuàng)建的資源,只要所引用的資源聲明為 final 或者是 effective final。

在幕后施展魔法的是 AutoCloseable 或者 Closeable 接口,它們與 try-with-resources 語(yǔ)句協(xié)同工作。

JDK 中大多數(shù)基于資源的類和接口已修改成實(shí)現(xiàn) AutoCloseable 或 Closeable 接口,使它們能與現(xiàn)有的 try-with-resources 兼容。

可以使自定義的資源實(shí)現(xiàn) AutoCloseable 或 Closeable 接口,以便能夠在 try-with-resources 語(yǔ)句中使用。

歡迎工作一到五年的Java工程師朋友們加入Java架構(gòu)開(kāi)發(fā): 854393687

群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來(lái)學(xué)習(xí)提升自己,不要再用"沒(méi)有時(shí)間“來(lái)掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來(lái)的自己一個(gè)交代!

為了讓學(xué)習(xí)變得輕松、高效,今天給大家免費(fèi)分享一套Java教學(xué)資源。幫助大家在成為Java架構(gòu)師的道路上披荊斬棘。需要資料的歡迎加入學(xué)習(xí)交流群:https://jq.qq.com/?_wv=1027&k=5KWnOM1

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容