要求: java 7 版本以上
测试环境: 1.8.0_102
首先自己建一个 MyCloseable 实现 java.io.Closeable,在关闭资源时打印一句话:”关闭资源”
public class MyCloseable implements Closeable {
@Override
public void close() throws IOException {
System.out.println("关闭资源");
}
}
新建测试类App ,看看这段源码反编译后做了什么
public class App {
public static void main(String[] args) {
try (MyCloseable myCloseable = new MyCloseable()) {
throw new RuntimeException("exception");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class App {
public App() {
}
public static void main(String[] args) {
try {
MyCloseable myCloseable = new MyCloseable();
Throwable var2 = null;
try {
} catch (Throwable var11) {
var2 = var11;
throw var11;
} finally {
if (myCloseable != null) {
if (var2 != null) {
try {
myCloseable.close();
} catch (Throwable var10) {
var2.addSuppressed(var10);
}
} else {
myCloseable.close();
}
}
}
} catch (Exception var13) {
var13.printStackTrace();
}
}
}
可以看出来 事实上 try-with-esource 实际上是java语法糖,它自己内部做了几个处理
1. 首先 new MyCloseable
2. 运行用户代码
3. 判断closeable 是否为空
- 为空: 什么也不做
- 不为空: 判断用户代码区是否有异常
- 没有异常: 执行MyCloseable里面的close
- 有异常: 执行MyCloseable里面的close,如果close发生异常就用用户的异常加上close的异常然后抛出
4. 执行用户的异常处理
public class App {
public static void main(String[] args) {
try (MyCloseable myCloseable = new MyCloseable()) {
System.out.println("完全没有异常");
} catch (Exception e) {
e.printStackTrace();
System.out.println("用户异常处理");
}
}
}
public class App {
public App() {
}
public static void main(String[] args) {
try {
MyCloseable myCloseable = new MyCloseable(); // 1
Throwable var2 = null;
try {
System.out.println("完全没有异常"); // 2
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (myCloseable != null) { // 3
if (var2 != null) { // 4
try {
myCloseable.close(); // 5
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
myCloseable.close();
}
}
}
} catch (Exception var14) {
var14.printStackTrace();
System.out.println("用户异常处理");
}
}
}
public class MyCloseable implements Closeable {
@Override
public void close() throws IOException {
System.out.println("关闭资源");
}
}
完全没有异常的情况下,执行完用户代码就做 Closeable为空判断和用户代码异常判断
最后执行关闭资源
public class App {
public App() {
}
public static void main(String[] args) {
try {
MyCloseable myCloseable = new MyCloseable(); // 1
Throwable var2 = null;
try {
throw new RuntimeException("用户代码异常"); // 2
} catch (Throwable var12) {
var2 = var12;
throw var12; // 3 // 7
} finally {
if (myCloseable != null) { // 4
if (var2 != null) { // 5
try {
myCloseable.close(); // 6
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
myCloseable.close();
}
}
}
} catch (Exception var14) {
var14.printStackTrace(); // 8
System.out.println("用户异常处理");
}
}
}
public class MyCloseable implements Closeable {
@Override
public void close() throws IOException {
System.out.println("关闭资源");
}
}
当用户代码有异常的时候,整体流程就麻烦了一些,接下来一步一步分析
1. MyCloseable myCloseable = new MyCloseable(); 创建的时候也在用户异常捕获里面,所以不用担心new对象发生异常不处理
2. 用户代码发生异常
3. 准备抛出异常,但是发现有finally代码区
4. 执行finally代码区, 判断 myCloseable 为空 (当前不为空)
5. 判断用户代码区是否有异常 (当前有异常)
6. 关闭资源
7. 抛出异常
8. 执行用户异常处理
public class App {
public App() {
}
public static void main(String[] args) {
try {
MyCloseable myCloseable = new MyCloseable(); // 1
Throwable var2 = null;
try {
System.out.println("用户资源没有异常"); //2
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (myCloseable != null) { // 3
if (var2 != null) { // 4
try {
myCloseable.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
myCloseable.close(); // 5
}
}
}
} catch (Exception var14) { // 6
var14.printStackTrace();
System.out.println("用户异常处理");
}
}
}
public class MyCloseable implements Closeable {
@Override
public void close() throws IOException {
throw new RuntimeException("关闭资源异常");
}
}
关闭资源异常流程分析
1. MyCloseable myCloseable = new MyCloseable(); 创建的时候也在用户异常捕获里面,所以不用担心new对象发生异常不处理
2. 用户资源没有异常
3. 执行finally代码区, 判断 myCloseable 为空 (当前不为空)
4. 判断用户代码区是否有异常 (当前没有异常)
5. 关闭资源(else 代码块),发生异常,被最外层的用户异常所捕获
6. 执行用户异常处理
public class App {
public App() {
}
public static void main(String[] args) {
try {
MyCloseable myCloseable = new MyCloseable(); // 1
Throwable var2 = null;
try {
throw new RuntimeException("用户代码异常"); // 2
} catch (Throwable var12) {
var2 = var12;
throw var12; // 3 // 8
} finally {
if (myCloseable != null) { // 4
if (var2 != null) { // 5
try {
myCloseable.close(); // 6
} catch (Throwable var11) {
var2.addSuppressed(var11); // 7
}
} else {
myCloseable.close();
}
}
}
} catch (Exception var14) { // 9
var14.printStackTrace();
System.out.println("用户异常处理");
}
}
}
public class MyCloseable implements Closeable {
@Override
public void close() throws IOException {
throw new RuntimeException("关闭资源异常");
}
}
用户代码和关闭资源都有异常
所以这里做了非常充分异常处理.
做了了用户资源是否为空的处理,避免了: MyCloseable myCloseable = null; 时,去关闭资源.
不会
如果想要关闭多个资源怎么办?
先关闭后面的还是先关闭前面的?
try (MyCloseable myCloseable = new MyCloseable();MyCloseable myCloseableA = new MyCloseable()) {
System.out.println("完全没有异常");
} catch (Exception e) {
e.printStackTrace();
System.out.println("用户异常处理");
}
反编译内容
public class App {
public App() {
}
public static void main(String[] args) {
try {
MyCloseable myCloseable = new MyCloseable();
Throwable var2 = null;
// 后面的 MyCloseable
try {
MyCloseable myCloseableA = new MyCloseable();
Throwable var4 = null;
try {
System.out.println("完全没有异常");
} catch (Throwable var29) {
var4 = var29;
throw var29;
} finally {
// 先处理后面的 myCloseableA
if (myCloseableA != null) {
if (var4 != null) {
try {
myCloseableA.close();
} catch (Throwable var28) {
var4.addSuppressed(var28);
}
} else {
myCloseableA.close();
}
}
}
} catch (Throwable var31) {
var2 = var31;
throw var31;
} finally {
// 后处理前面的 myCloseable
if (myCloseable != null) {
if (var2 != null) {
try {
myCloseable.close();
} catch (Throwable var27) {
var2.addSuppressed(var27);
}
} else {
myCloseable.close();
}
}
}
} catch (Exception var33) {
var33.printStackTrace();
System.out.println("用户异常处理");
}
}
}
这里和单资源关闭多了一点关于异常信息的内容:
用户异常 + 最后面的close异常 + 最前面的close异常信息。
会先关闭后面的,在关闭前面的资源.
所以如果关闭多个资源时,对关闭顺序有要求的情况,一定不要写反了。
最后面的最先关闭,最前面的最后关闭