背景: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