Java类库里包含了必须经过调用close方法来手动关闭的资源。好比InputStream,OutputStream还有java.sql.Connection。java
关闭资源这个动做一般被客户端忽视了,其性能表现也可想而知。虽然大部分这些资源都使用终结方法做为最后的安全线,但终结方法的效果并非很好。sql
在过去(Java7以前)的实践当中,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();
}
}复制代码
这么作看起来可能还没什么问题,但当你添加第二个资源时,状况就开始变得糟糕了:
bash
// 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();
}
}复制代码
即便对于正确使用了try-finally语句的代码,如前面所示,也有个不起眼的缺点。不管是try里面的代码仍是finally里面的代码,都有可能抛出异常。
ide
不管是try里面的代码仍是finally里面的代码,都有可能抛出异常。例如,在firstLineOfFile方法里,若是底层物理设备出了故障,则在调用readLine方法时会抛出异常,并且因为相同的缘由,调用close方法也会失败。在这种状况下,第二种异常覆盖了第一种异常。在异常错误栈里将没有第一种异常的记录,这会使实际系统的调试变得很复杂,由于不少时候你是想查看第一种异常来诊断问题。(异常屏蔽的状况)
性能
当Java 7引入try-with-resources
语句时,全部问题忽然一会儿解决了。ui
若要使用这个语句,一个资源必须实现AutoCloseable接口,而这个接口只有一个返回类型为void的close(void-returning)方法。spa
意味着可能抛出 java.lang.Exception。
然而,前面的 AutoClose 示例对该方法进行声明,但并未说起任何检查到的异常,这是咱们有意为之,部分是为了说明异常屏蔽。
Java类库和第三方类库里面的许多类和接口如今都实现或继承了AutoCloseable接口。若是你写了一个类,这个类表明一个必须被关闭的资源,那么你的类也应该实现AutoCloseable接口。
线程
可自动关闭类的规范建议避免抛出
java.lang.Exception,优先使用具体的受检异常,若是预计 close() 方法不会失败,就没必要说起任何受检异常。此外还建议,不要声明任何不该被抑制的异常,java.lang.InterruptedException 就是最好的例子。
实际上,抑制该异常并将其附加到另外一个异常可能会致使忽略线程中断事件,使应用程序处于不一致的状态。调试
这是咱们自主实现AutoCloseable的一个例子:
/**
* 资源类
*/public class Resource implements AutoCloseable {
public void invoke() {
// todo 业务处理
System.out.println("Resource is using");
}
@Override
public void close() throws Exception {
// todo 关闭资源
System.out.println("Resource is closed");
}
}复制代码
public class CloseResource {
public static void main(String[] args) {
try(Resource resource = new Resource()) {
resource.invoke();
} catch (Exception e) {
e.printStackTrace();
}
}
}复制代码
下面这个例子展现了如何使用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-finally语句那样,往try-with-resources里面添加catch子句。
这能让咱们无需在另外一层嵌套污染代码就能处理异常。下面是一个比较刻意的例子,这个版本中的firstLineOfFile方法不会抛出异常,但若是它不能打开文件或者不能读打开的文件,它将返回一个默认值:
// 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则几乎作不到这点。