try-with-resource
是 Java 7 引入的一个特性,用于简化资源管理。在处理像文件、网络连接、数据库连接这类需要手动关闭的资源时,传统的 try-catch-finally
结构可能会让代码变得复杂,而且容易因为忘记关闭资源而导致资源泄漏。try-with-resource
可以自动关闭实现了 AutoCloseable
接口的资源,让代码更简洁、安全。
某次手动强制停止下面问题代码任务,再重新触发后,出现连接泄露报错,查资料后发现,存在的问题有:1.在请求失败和抛出异常的情况下连接资源都不能正确关闭
问题代码:
ini
try {
OkHttpClient client = new OkHttpClient()
.newBuilder()
.connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.authenticator((route, response) -> {
String credential = xxxx;
return response.request().newBuilder().header("Authorization", credential).build();
}).build();
String requestUrl = xxxxxxxxxxx;
Request request = new Request.Builder()
.url(requestUrl)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
String res = response.body().string();
return res;
} else {
logger.error("请求失败,状态码: {}", response.code());
return null;
}
} catch (IOException e) {
logger.error("捕获异常");
return null;
}
解决:使用try-with-resource
自动关闭资源,修改后:
ini
OkHttpClient client = new OkHttpClient()
.newBuilder()
.connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.authenticator((route, response) -> {
String credential = xxxx;
return response.request().newBuilder().header("Authorization", credential).build();
}).build();
String requestUrl = xxxxxxxxxxxxxxxxxxx;
Request request = new Request.Builder()
.url(requestUrl)
.build();
try (Response response = client.newCall(request).execute()) {
ResponseBody body = response.body();
if (body == null) {
logger.error("响应体为空");
return null;
}
if (response.isSuccessful()) {
String res = body.string();
return res;
} else {
logger.error("请求失败,状态码: {}", response.code());
return null;
}
} catch (IOException e) {
logger.error("捕获异常");
return null;
}
!!!请我以后习惯性使用try with resources
什么时候使用try with resources
可以用 try with resources关闭的条件是什么
一个对象要能够被 try-with-resources
语句管理(即可以放在 try (...)
的括号中),必须满足一个核心条件 :该对象的类必须直接或间接实现 java.lang.AutoCloseable
接口。(上面的Response类是实现了Closeable类,而Closeable实现了AutoCloseable)
-
AutoCloseable
接口-
这是 Java 7 引入的接口,是
try-with-resources
语句的基础 , 任何实现了AutoCloseable
的类,都可以被try-with-resources
管理。 -
它只定义了一个方法:
csharppublic void close() throws Exception;
-
-
Closeable
接口-
java.io.Closeable
是AutoCloseable
的子接口。 -
它重写了
close()
方法,抛出更具体的IOException
:
csharppublic void close() throws IOException;
-
-
结论 :所有实现了
Closeable
的类,也自动实现了AutoCloseable
,因此也支持try-with-resources
。所以,只要一个类实现了AutoCloseable
或Closeable
,就可以用try-with-resources
。
常见支持 try-with-resources
的类型示例
类型 | 是否支持 | 说明 |
---|---|---|
InputStream / OutputStream |
✅ 是 | 实现 Closeable |
Reader / Writer |
✅ 是 | 实现 Closeable |
Socket |
✅ 是 | 实现 Closeable |
ServerSocket |
✅ 是 | 实现 Closeable |
RandomAccessFile |
✅ 是 | 实现 Closeable |
java.sql.Connection |
✅ 是 | 实现 AutoCloseable |
java.sql.Statement |
✅ 是 | 实现 AutoCloseable |
java.sql.ResultSet |
✅ 是 | 实现 AutoCloseable |
okhttp3.Response |
✅ 是 | 实现 Closeable |
java.util.zip.ZipFile |
✅ 是 | 实现 Closeable |
java.nio.channels.Channel |
✅ 是 | 实现 Closeable |
java.util.Scanner |
✅ 是 | 实现 Closeable |
❌ 不支持 try-with-resources
的示例
rust
java
编辑
String str = "Hello";
try (str) { // ❌ 编译错误!String 没有实现 AutoCloseable
// ...
}
✅ try-with-resources
的语法形式
java
// 单个资源
try (Resource resource = new Resource()) {
// 使用 resource
} // 自动调用 resource.close()
// 多个资源(Java 7+)
try (Resource1 r1 = new Resource1();
Resource2 r2 = new Resource2()) {
// 使用 r1 和 r2
} // 按逆序自动关闭:r2.close() 然后 r1.close()
// 多个资源(Java 9+ 支持变量引用)
Resource1 r1 = new Resource1();
Resource2 r2 = new Resource2();
try (r1; r2) { // Java 9+ 允许直接使用已初始化的变量
// ...
} // 自动关闭
⚠️ 重要特性
-
自动关闭:
- 无论
try
块是正常结束还是抛出异常,close()
方法都会被调用。
- 无论
-
异常处理:
- 如果
try
块抛出异常,且close()
方法也抛出异常,try
块的异常会作为主异常抛出,close()
的异常会被抑制(suppressed) ,可以通过Throwable.getSuppressed()
获取。
- 如果
-
关闭顺序:
- 多个资源时,按声明的逆序关闭(后声明的先关闭)。
建议
- 当你需要使用需要手动关闭的资源时,优先考虑使用try-with-resource` 来简化代码和避免资源泄漏。
- 可以在
try
的括号里声明多个资源,它们会按照声明的相反顺序依次关闭。
你可以尝试修改示例代码中的文件名,或者添加更多资源,进一步理解 try-with-resource
的用法。