try-with-resource是从java7开始提供的新特性。方便了我们资源的代码编写。下面展示一下代码模板的对比
没有try-with-resource
try{ 资源打开 业务逻辑 }catch(Exception e){ 异常处理 }finally{ 资源关闭 }
try-with-resource的
try(资源打开){ 业务逻辑 }catch(Exception e){ 异常处理 }
看了上面的模板,我们的一个直观感觉是省去了finally,并且把资源放在了try后面的括号中。下面展示一段真实的代码。
public static void main(String[] args) { try ( InputStream fileInputStream = Files.newInputStream(Paths.get("/Users/xie/delete/test.txt")); OutputStream outputStream = Files.newOutputStream(Paths.get("/Users/xie/delete/test2.txt")); ) { byte[] bytes = new byte[1024]; int len; while ((len = fileInputStream.read(bytes)) > 0) { outputStream.write(bytes, 0, len); } } catch (IOException e) { e.printStackTrace(); } }
注:上面的代码只是演示了一个文件拷贝的方法,如果我们自己写代码请选择1.7提供的Files.copy。
我们通过反编译工具来做一下对比
public static void main(String[] args) { try { InputStream fileInputStream = Files.newInputStream(Paths.get("/Users/xie/delete/test.txt")); Throwable var2 = null; try { OutputStream outputStream = Files.newOutputStream(Paths.get("/Users/xie/delete/test2.txt")); Throwable var4 = null; try { byte[] bytes = new byte[1024]; int len; while((len = fileInputStream.read(bytes)) > 0) { outputStream.write(bytes, 0, len); } } catch (Throwable var30) { var4 = var30; throw var30; } finally { if (outputStream != null) { if (var4 != null) { try { outputStream.close(); } catch (Throwable var29) { var4.addSuppressed(var29); } } else { outputStream.close(); } } } } catch (Throwable var32) { var2 = var32; throw var32; } finally { if (fileInputStream != null) { if (var2 != null) { try { fileInputStream.close(); } catch (Throwable var28) { var2.addSuppressed(var28); } } else { fileInputStream.close(); } } } } catch (IOException var34) { var34.printStackTrace(); } }
基本从字节码反编译过来,我们可以看到他是按照我们熟悉的方式编写的代码。所以try-with-resource是语法糖。
这里大家不熟悉的可能就是一场处理里面有addSuppressed的调用。这是一个异常新加的方法,抑制异常。其实这种场景很常见,一个方法里执行出了多个异常,应该报哪个呢,虽然都能表示这个方法调用的失败。以前的做法就是自己catch然后做一些逻辑操作,最后抛出一个。有了这个方法就能实现以前的逻辑,逻辑中也可以取出被抑制的异常信息。
class OutputStream implements Closeable, Flushable {
只有实现了Closeable的才可以和try-with-resource搭配使用。
class SocketInputStream extends FileInputStream
但是他的close方法做的事情有点多,会检测socket是否关闭。
public void close() throws IOException { if (closing) return; closing = true; if (socket != null) { if (!socket.isClosed()) socket.close(); } else impl.close(); closing = false; }
上面的这些情况,本身就是使用流该注意的地方。
java9之前的try-with-resource都是必须做一次赋值的。
public static void read(InputStream fileInputStream) { try (InputStream fileInputStreamTmp = fileInputStream) { byte[] bytes = new byte[1024]; int len; while ((len = fileInputStreamTmp.read(bytes)) > 0) { } } catch (IOException e) { e.printStackTrace(); } }
这个问题也挺明显的,我都开始名字上加Tmp来标识了。
9之后就彻底不用这么做了
public static void read(InputStream fileInputStream) { try (fileInputStream) { byte[] bytes = new byte[1024]; int len; while ((len = fileInputStream.read(bytes)) > 0) { } } catch (IOException e) { e.printStackTrace(); } }