0
点赞
收藏
分享

微信扫一扫

使用 try-with-resources 优雅关闭资源

源码之路 2022-12-03 阅读 143


try-with-resources​: 语句确保在语句的最后每个资源都被关闭, 无需手动(显示)去关闭资源连接

 

public class AnswerApp {
// try-with-resources 案例
private static void answer1(String path) {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
String line;
while ((line = (br.readLine())) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}

// try {} catch {} finally {} 案例, 需手动关闭资源
private static void answer2(String path) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
String line;
while ((line = (br.readLine())) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 需要手动在 finally 代码块中关闭资源连接
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

}

 

反编译后代码如下

private static void answer1(String path) {
try {
BufferedReader br = new BufferedReader(new FileReader(path));
Throwable var2 = null;

try {
String line;
try {
while((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Throwable var12) {
// 业务处理时跑出的异常
var2 = var12;
throw var12;
}
} finally {
if(br != null) {
if(var2 != null) {
try {
br.close();
} catch (Throwable var11) {
// 关闭连接时如果抛出异常, 则该异常会被 业务处理时抛出的异常所 抑制
var2.addSuppressed(var11);
}
} else {
br.close();
}
}

}
} catch (IOException var14) {
var14.printStackTrace();
}
}

private static void answer2(String path) {
BufferedReader br = null;

try {
br = new BufferedReader(new FileReader(path));

String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException var11) {
var11.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException var10) {
var10.printStackTrace();
}
}

}
}

 

打开多个资源

public class AnswerApp {

// try {} catch {} finally {} 案例, 需手动关闭资源
private static void answer3(String path) {
BufferedReader br = null;
BufferedWriter bw = null;

try {
br = new BufferedReader(new FileReader(path));
bw = new BufferedWriter(new FileWriter(path + "_bak"));

String line;
while ((line = (br.readLine())) != null) {
bw.write(line);
bw.newLine();
}
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 资源的 close 方法调用顺序与它们的创建顺序相反
if (null != bw) {
bw.close();
}

if (null != br) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

// try-with-resources 案例
private static void answer4(String path) {
// 如果打开多个资源的话, 关闭资源的顺序 和 创建资源的顺序 相反
try (BufferedReader br = new BufferedReader(new FileReader(path));
BufferedWriter bw = new BufferedWriter(new FileWriter(path + "_bak"))) {

String line;
while ((line = (br.readLine())) != null) {
bw.write(line);
bw.newLine();
}
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}

 

Resource的定义​: 所有实现了 java.lang.AutoCloseable 接口(其中: 它包括实现了 java.io.Closeable 的所有对象), 可以使用作为资源

public class Mysql implements AutoCloseable {

public void connect() {
System.out.println("mysql connect...");
}

@Override
public void close() throws Exception {
System.out.println("mysql connect close!!!");
}
}

public class Redis implements AutoCloseable {

public void connect() {
System.out.println("redis connect...");
}

@Override
public void close() throws Exception {
System.out.println("redis connect close!!!");
}
}

 

public class AnswerApp {
// 验证: 资源的 close 方法调用顺序与它们的创建顺序相反
public static void main(String[] args) {
try (Mysql mysql = new Mysql();
Redis redis = new Redis()) {

mysql.connect();
redis.connect();
} catch (Exception e) {
e.printStackTrace();
}
}
}

 

​程序运行输出​

mysql connect...
redis connect...
redis connect close!!!
mysql connect close!!!

从输出结果看: 资源的 close 方法调用顺序与它们的创建顺序相反

  • 创建顺序:
  1. new Mysql()
  2. new Redis()
  • 关闭顺序:
  1. redis.close()
  2. mysql.close()

 

try-with-resource 机制是一种语法糖, 其底层实现原理仍然是 try{…}catch(){…}finally{…} 写法(可通过反编译查看), 不过在 catch(){} 代码块中有一个 addSuppressed() 方法, 即异常抑制方法。 如果业务处理和关闭连接都出现了异常, 业务处理的异常会抑制关闭连接的异常, 只抛出处理中的异常, 仍然可以通过 e.getSuppressed() 方法获得关闭连接的异常

 

异常抑制: 当对外部资源进行处理(例如读或写)时, 如果遭遇了异常, 且在随后的关闭外部资源过程中, 又遭遇了异常, 那么你catch到的将会是对外部资源进行处理时遭遇的异常, 关闭资源时遭遇的异常将被“抑制”但不是丢弃, 通过异常的 e.getSuppressed 方法, 可以提取出被抑制的异常

  • 业务异常 和 关闭资源连接异常 都存在: 捕获的异常为 业务处理异常, 关闭资源异常将被抑制
  • 只存在 关闭资源连接异常(多个): 捕获的异常为 第一个(​​资源的 close 方法调用顺序与它们的创建顺序相反​​)关闭资源连接抛出异常, 其他的将被抑制

获取被抑制异常: 通过 e.getSuppressed() 方法 获取被抑制异常信息

 

public class Mysql implements AutoCloseable {

public void connect() {}

@Override
public void close() throws Exception {
// 模拟 Mysql 关闭连接时异常
throw new Exception("Mysql close Exception");
}
}

public class Redis implements AutoCloseable {

public void connect() {}

@Override
public void close() throws Exception {
// 模拟 Redis 关闭连接时异常
throw new Exception("Redis close Exception");
}
}

 

业务异常 和 关闭资源连接异常 都存在

public class AnswerApp {
public static void main(String[] args) {
try (Mysql mysql = new Mysql();
Redis redis = new Redis()) {
mysql.connect();
redis.connect();
throw new Exception("业务处理异常");
} catch (Exception e) {
// 异常对象中只打印 业务流程 上抛出的异常
System.out.println("msg: " + e.getMessage());

// Mysql&Redis 关闭连接时抛出的异常被抑制, 需要调用 getSuppressed 方法来获取被抑制的异常
for (Throwable t: e.getSuppressed()) {
System.out.println(t.getMessage());
}
}
}
}

 

​程序运行输出​

msg: 业务处理异常
Redis close Exception
Mysql close Exception

 

只存在 关闭资源连接异常

public class AnswerApp {
public static void main(String[] args) {
try (Mysql mysql = new Mysql();
Redis redis = new Redis()) {
mysql.connect();
redis.connect();
} catch (Exception e) {
// 异常对象中只打印 业务流程 上抛出的异常
System.out.println("msg: " + e.getMessage());

// Mysql&Redis 关闭连接时抛出的异常被抑制, 需要调用 getSuppressed 方法来获取被抑制的异常
for (Throwable t: e.getSuppressed()) {
System.out.println(t.getMessage());
}
}
}
}

 

​程序运行输出​

msg: Redis close Exception
Mysql close Exception


举报

相关推荐

0 条评论