Effective java 系列之更優雅的關閉資源-try-with-resources

背景:html

在Java編程過程當中,若是打開了外部資源(文件、數據庫鏈接、網絡鏈接等),咱們必須在這些外部資源使用完畢後,手動關閉它們。由於外部資源不禁JVM管理,沒法享用JVM的垃圾回收機制,若是咱們不在編程時確保在正確的時機關閉外部資源,就會致使外部資源泄露,緊接着就會出現文件被異常佔用,數據庫鏈接過多致使鏈接池溢出等諸多很嚴重的問題。數據庫

傳統關閉資源方式(通常JDK版本低於1.7)編程

/** * 根據路徑建立文件,若是路徑文件夾不存在,就建立 * @param filePath * @return */
    public static File createFileAbsolute(String filePath) throws IOException { File file = new File(filePath); File fileParent = file.getParentFile(); if(!fileParent.exists()){ fileParent.mkdirs(); } file.createNewFile(); return file; } /** * 傳統寫法,JDK1.6 * @param dataStr * @return */
    public static long write2FileTradition(String dataStr,String filePath){ long fileSize = 0L; RandomAccessFile raf = null; FileChannel fchannel = null; try { raf = new RandomAccessFile(createFileAbsolute(filePath),"rw"); fchannel = raf.getChannel(); ByteBuffer buf = ByteBuffer.allocate(1024); buf.clear(); buf.put(dataStr.getBytes()); buf.flip(); while(buf.hasRemaining()){ fchannel.write(buf); } fileSize = fchannel.size(); } catch (FileNotFoundException e) { logger.error("file not found",e); } catch (IOException e) { logger.error("IO exception",e); }finally{
       IOUtils.close(fchannel); IOUtils.close(raf); }
return fileSize; }

注意:若是是節點流和處理流,可直接關閉處理流,由於io流使用了裝飾模式,因此關閉處理流時,會調用節點流的close()方法。網絡

JDK1.7之後關閉資源的方式dom

try-with-resource語法(語法糖)spa

在JDK7之前,Java沒有自動關閉外部資源的語法特性,直到JDK7中新增了try-with-resource語法,才實現了這一功能。code

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

/** * JDK1.7 關閉資源語法糖 * * @param dataStr * @param filePath * @return
     */
    public static long write2File(String dataStr, String filePath) { long fileSize = 0L; try ( RandomAccessFile raf = new RandomAccessFile(createFileAbsolute(filePath), "rw"); FileChannel fchannel = raf.getChannel() ) { ByteBuffer buf = ByteBuffer.allocate(1024); buf.clear(); buf.put(dataStr.getBytes()); buf.flip(); while (buf.hasRemaining()) { fchannel.write(buf); } fileSize = fchannel.size(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return fileSize; }

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

反編譯後源碼:blog

public static long write2File(String dataStr, String filePath) { long fileSize = 0L; try { RandomAccessFile raf = new RandomAccessFile(filePath, "rw"); Throwable var5 = null; try { FileChannel fchannel = raf.getChannel(); Throwable var7 = null; try { ByteBuffer buf = ByteBuffer.allocate(1024); buf.clear(); buf.put(dataStr.getBytes()); buf.flip(); while(buf.hasRemaining()) { fchannel.write(buf); } fileSize = fchannel.size(); } catch (Throwable var34) { var7 = var34; throw var34; } finally { if (fchannel != null) { if (var7 != null) { try { fchannel.close(); } catch (Throwable var33) { var7.addSuppressed(var33); } } else { fchannel.close(); } } } } catch (Throwable var36) { var5 = var36; throw var36; } finally { if (raf != null) { if (var5 != null) { try { raf.close(); } catch (Throwable var32) { var5.addSuppressed(var32); } } else { raf.close(); } } } } catch (FileNotFoundException var38) { logger.error("file not found", var38); } catch (IOException var39) { logger.error("IO exception", var39); } return fileSize; }

異常抑制

經過反編譯的代碼,你們可能注意到代碼中有一處對異常的特殊處理:

var2.addSuppressed(var11);

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

這裏理解一點,就是try塊中拋出的異常,在finally也拋出異常時,就會丟失,因此一般在finally不拋異常,而是經過 addSuppressed()方法將異常抑制住。

 

總結:

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

二、try-with-resource時,若是對外部資源的處理和對外部資源的關閉均遭遇了異常,「關閉異常」將被抑制,「處理異常」將被拋出,但「關閉異常」並無丟失,而是存放在「處理異常」的被抑制的異常列表中。

 

參考:

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

https://www.cnblogs.com/langtianya/p/5139465.html

相關文章
相關標籤/搜索