Resource的release 是在使用resource物件時,所特別需要注意的事,小則AP掛掉,大則機器損毀;所以凡是開發人員,在使用resource物件時,都要特別小心,像是Connection
、File
、I/O Stream
等。可是根據莫菲定律,越擔心會發生的事就越會發生,code寫久了,總有一天你會忘記release resource…
適合閱讀
本篇適合以下人員閱讀:
- 知道Java不只是咖啡的人
- 會寫Hello Java的人
好吧,如果你不會寫程式,也可以看看,提供一些意見;如果你是dot Net高手,也請不吝指教。
正文
開發人員在開發的時候,除了bug之外,最怕的就是忘記關閉一些已使用資源物件。 1.7 版本以後,所有的Closeable
物件都繼承了AutoCloseable
;也同時提供了一個新的語法:try-with-resoure statement。 今天主要介紹這個try-with-resource的用法。
我們一般的try-catch寫法是這樣的:
public void normalTry() throws Exception {
String sql = "This is a sql statement with parameter";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = getConnection();// Any methodology to get connection.
ps = conn.prepareStatement(sql);
ps.setString(1, "This is parameter");
rs = ps.executeQuery();
while (rs.next()) {
rs.getString(1);
}
} finally {
try {
rs.close();
ps.close();
conn.close();
} catch (Exception e) {
throw e;
}
}
}
一般的try-catch-finally寫法,物件的宣告必需在try-catch之前,而且必需要有個finally來releaseCloseable
的物件。1.7 以後你可以在try後面增加(
)
並在裡面放置一些需要close的物件。 這裡是一個try-with-resource的範例:
public void tryWithResource() throws Exception {
String sql = "This is a sql statement";
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "This is parameter");
try (ResultSet rs = ps.executeQuery(sql);) {
while (rs.next()) {
rs.getString(1);
}
}
}
}
使用新的try-with-resource寫法,程式行數由23行減為10行。
Try-with-resource也可以像一般try-catch的方式,搭配catch與finally服用。 簡單來說try-with-resource statement,就是這樣。
不過事情並不是這麼單純。
進階
我們把進階分成優點跟缺點來看…
優點
Try-with-resource statement 可以防止開發人員因為忘記關閉resource導致系統 crash。
還記得我們一開始提到
AutoCloseable
嗎? 在try 裡面的東西不是阿貓阿狗都可以放進來,他必需要是AutoCloseable
的物件。原本1.6以前的Closeable
物件(其實是Interface),現在已經直接繼承AutoCloseable
,所以所有的Closeable
物件都是AutoCloseable
物件。 JVM會在離開try之後依建構的反向順序 呼叫AutoCloseable.close()
。PS:
Closeable
、AutoCloseable
都是interface。可以減少程式碼。 不言自明,由範例來看,程式碼很明顯的減少了。
缺點
程式碼可能不容易閱讀。
tryWithResource
的範例中,ResultSet
必需在PreapreStatement
設定參數後才能執行,才能建構,所以沒辦法放與Connection
、PrepareStatement
放在同一個try()
括號中,因此出現在巢狀的try敘述。 也許看久了會習慣,不過以現時的我來說,還沒那麼習慣。當然,這純屬個人偏好;但想像一下,這種情形如果重覆很多層,有時候,真的會很難搞清楚最後一個大括號,到底是對應那一個。當其他錯誤與
close()
同時發生錯誤的話,close()
的錯誤會被蓋掉。考慮以下程式碼:
public void suppressException() throws Exception { String sql = "This is a sql statement"; try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { if (allwaysReturnTrue()) { throw new OperationNotSupportedException(); } }
假設當
Connection
在close()
時發生錯誤,log只會看到OperationNotSupportedException()
所拋出的錯誤,不會看到close()
的錯誤。不過這有方法可以處理,就是使用Throwable.getSuppressed()
。public void catchSuppressException() throws Exception { String sql = "This is a sql statement"; try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { if (allwaysReturnTrue()) { throw new OperationNotSupportedException(); } } } public static void main(String[] args) { TryWithRessourceDemo d=new TryWithRessourceDemo(); try { d.catchSuppressException(); } catch (Throwable e) { System.out.println("main"); System.out.println(e); Throwable[] suppressed = e.getSuppressed();//Use getSuppressed() to find suppressed exception for (int i = 0; i < suppressed.length; i++) { Throwable throwable = suppressed[i]; System.out.println(throwable); } } }
小結
本篇介紹了try-with-resource的用法與特性,好用,但用得太過很容造成閱讀障礙。 雖然close()
不太容易會發生錯誤,但要注意suppressed exception的問題
同場加映
一開始學Java時,只道try-catch必需成雙成對,最多是只有finally可以選擇性的加與不加; 有人知道catch與finally都可以選擇加與不加嗎?
//我以為try-catch只有2種寫法
try{...} catch(){...}
try{...} catch(){...} finally{...}
//偶然看到高手這麼用
try{...} finally{...}
沒有留言 :
張貼留言