新语法
在java7之前,释放资源的一般写法如下
public String readFirstLine(String path) throws IOException {
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(path);
br = new BufferedReader(fr);
return br.readLine();
} finally {
if (br != null) {
br.close();
}
if (fr != null) {
fr.close();
}
}
}
java7引入了try-with-resource语法,上面的逻辑可改写为
public String readFirstLine(String path) throws IOException {
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
return br.readLine();
}
}
新语法需要资源类实现Closeable或AutoCloseable接口。
Java做出这种语法优化的原因
- 旧写法br.close()方法执行抛出异常时,依然存在资源泄露的可能
- 旧写法try块和finally块均抛出异常时,try块的异常被覆盖,抛出去的是finally块的异常
- 比较之下,新写法比旧写法更简洁,可读性更好
举例说明,比如资源类A与资源类B如下:
public class A implements AutoCloseable {
public String readLine() {
throw new UnsupportedOperationException("Failed to read A");
}
@Override
public void close() {
throw new RuntimeException("Failed to close A");
}
}
@AllArgsConstructor
public class B implements AutoCloseable {
private A a;
public String readLine() {
return a.readLine();
}
@Override
public void close() {
throw new RuntimeException("Failed to close B");
}
}
旧写法下资源读取及关闭:
public class TryWithResource {
public String readFirstLineOldWay() {
A a = null;
B b = null;
try {
a = new A();
b = new B(a);
return b.readLine();
} finally {
if (a != null) {
a.close();
}
if (b != null) {
b.close();
}
}
}
public static void main(String[] args) {
new TryWithResource().readFirstLineOldWay();
}
}
旧写法执行结果为:
Exception in thread "main" java.lang.RuntimeException: Failed to close A
at com.czy.demo.A.close(A.java:11)
at com.czy.demo.TryWithResource.readFirstLineOldWay(TryWithResource.java:21)
at com.czy.demo.TryWithResource.main(TryWithResource.java:31)
可见:
- a.close() 抛异常,b.close() 未执行,b 存在资源泄露的可能
- 抛出异常为finally块的RuntimeException,try块中的UnsupportedOperationException被覆盖
再看下新写法下资源读取及关闭:
public class TryWithResource {
public String readFirstLineNewWay() {
try (A a = new A();
B b = new B(a)) {
return b.readLine();
}
}
public static void main(String[] args) {
new TryWithResource().readFirstLineNewWay();
}
}
新写法执行结果为:
Exception in thread "main" java.lang.UnsupportedOperationException: Failed to read A
at com.czy.demo.A.readLine(A.java:6)
at com.czy.demo.B.readLine(B.java:10)
at com.czy.demo.TryWithResource.readFirstLineNewWay(TryWithResource.java:8)
at com.czy.demo.TryWithResource.main(TryWithResource.java:30)
Suppressed: java.lang.RuntimeException: Failed to close B
at com.czy.demo.B.close(B.java:15)
at com.czy.demo.TryWithResource.readFirstLineNewWay(TryWithResource.java:9)
... 1 more
Suppressed: java.lang.RuntimeException: Failed to close A
at com.czy.demo.A.close(A.java:11)
at com.czy.demo.TryWithResource.readFirstLineNewWay(TryWithResource.java:9)
... 1 more
- b.close()与a.close()均执行(按创建逆序执行)
- 抛出异常为try块的UnsupportedOperationException,finally块的异常以Suppressed Exception的方式保存在抛出异常中(Throwable#getSuppressed获取)。
try-with-resoure写法要注意的一个小点
资源A与资源B如下:
public class A implements AutoCloseable {
public String readLine() {
return "A";
}
@Override
public void close() {
System.out.println("A");
}
}
@AllArgsConstructor
public class B implements AutoCloseable {
private A a;
public String readLine() {
return a.readLine();
}
@Override
public void close() {
System.out.println("B");
}
}
通过console输出可以清楚地看到下面两种方法关闭资源的方式不同:
/**
* console输出为:
* B
* A
*/
public String readFirstLineNewWay() {
try (A a = new A();
B b = new B(a)) {
return b.readLine();
}
}
/**
* console输出为:
* B
*/
public String readFirstLineWrongWay() {
try (B b = new B(new A())) {
return b.readLine();
}
}
就是说,被try-with-resource自动执行close方法关闭资源,需要资源类的创建语句在try中依次声明,分号";"来分隔。
不过对于像BufferedReader这种java.io的资源类,close()方法的实现里都会执行源资源类的close()方法,所以文章开头的写法是没问题的。
总结
- 使用新语法关闭资源
- 需要自动执行close()方法的资源类依次声明在try块中,就不用考虑方法的实现了
- try-with-resource 语法 resource with trytry-with-resource语法resource java try-with-resource语法resource with try-with-resource try-with-resources try-with-resources resources java with try-with-resources妙招resources代码 try-with-resource resource方式 资源 try-with-resource resource java with try-with-resources resources方式 资源 try-with-resources语句resources with