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 方法调用顺序与它们的创建顺序相反
- 创建顺序:
- new Mysql()
- new Redis()
- 关闭顺序:
- redis.close()
- 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