学习笔记@Effective Java
文章内容来源于Joshua Bloch - Effective Java (3rd) - 2018.chm一书
第二章
创建和注销对象
Item 9优先使用try-with-resources相比于try-finally
Java库包含许多必须通过调用close方法手动关闭的资源。示例包括InputStream、OutputStream和java.sql.Connection连接. 关闭资源常常被客户忽略,这会带来可怕的性能后果。虽然这些资源中有许多将终结器用作安全网,但Finalizer的情况并不好
历史上,try finally语句是保证资源正确关闭的最佳方式,即使在遇到异常或返回时也是如此:
// try-finally - No longer the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}
这看起来没问题,但添加第二个资源时情况会变糟:
// try-finally is ugly when used with more than one resource!
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
这可能很难让人相信,但即使是优秀的程序员在大多数情况下也犯了这个错误。
事实上,在2007年,Java库中close方法的使用有三分之二是错误的。
即使使用try finally语句关闭资源的正确代码(如前两个代码示例所示)也有一个微妙的缺陷。try块和finally块中的代码都能够抛出异常
当Java7引入try with resources语句时,这些问题都解决了
要使用此构造,资源必须实现AutoCloseable接口,该接口由单个空返回的close方法组成。Java库和第三方库中的许多类和接口现在实现或扩展了AutoCloseable
如果您编写了一个表示必须关闭的资源的类,那么您的类就应该实现AutoCloseable。
下面是使用try with resources的第一个示例:
// try-with-resources - the the best way to close resources!
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
}
}
下面是第二个示例如何使用try with resources:
// try-with-resources on multiple resources - short and sweet
static void copy(String src, String dst) throws IOException {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
try-with-resources版本不仅比try-finally更短、可读性更高,而且提供了更好的报错诊断
举例firstLineOfFile 方法
如果一个异常既被readLine调用还被(不可见的)close抛出,那么后面的异常将被抑制,而只有前者抛出
实际上,可以抑制多个异常,以便保留您实际希望看到的异常。这些被抑制的异常不仅仅被丢弃;它们被打印在堆栈跟踪中,并带有一个表示它们被抑制的符号
你可以使用getSuppressed方法以编程方式访问它们
可以将catch子句放在try with resources语句上,就像放在常规try finally语句上一样。这使您能够处理异常,而不会因为一层嵌套而影响代码
作为一个有点不自然的示例,以下是我们的firstlineofile方法的一个版本,它不会抛出异常,但如果无法打开文件或从中读取文件,则会返回一个默认值:
// try-with-resources with a catch clause
static String firstLineOfFile(String path, String defaultVal) {
try (BufferedReader br = new BufferedReader(
new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
教训很清楚:在处理必须关闭的资源时,总是优先使用try with resources,而不是try finally。生成的代码更短、更清晰,它生成的异常也更有用。try with resources语句使得使用必须关闭的资源编写正确的代码变得容易,而使用try finally实际上是做不到这些