java中的try-with-resource语法

java的世界千奇百怪。。。当我甩出如下代码段,不知阁下如何应对?

java 复制代码
try(A a=new A()){
    和a变量无关的业务代码块
}

没错,这就是"臭名昭著"的try-with-resource语法,乍一看让人不知所云,其实它和try-finally的下述代码等价

java 复制代码
A a=new A()
try{
    //业务代码块
}finally{
    a.close();
}

本质上就是当使用 try-with-resources 语句块来处理实现了 AutoCloseable 接口的资源时,Java 编译器会在生成的字节码中进行特殊处理。编译器会自动在 try 块结束时插入 finally 块,并在 finally 块中调用 close() 方法来关闭资源。

try-with-resources语法

try-with-resources 语句是一个 Java 7 引入的语法结构,用于自动关闭实现了 AutoCloseableCloseable 接口的资源。它的语法形式如下:

java 复制代码
try (resource_declaration) {
    // 使用资源的代码块
} catch (ExceptionType e) {
    // 异常处理逻辑
}

try-with-resources 语句中,resource_declaration 部分用于声明和初始化一个或多个资源对象。这些资源对象必须实现 AutoCloseableCloseable 接口。

try 代码块中,可以使用这些资源对象进行操作。当 try 代码块执行结束时,不论是正常结束还是发生异常,都会自动调用资源对象的 close() 方法来关闭资源。

如果同时声明了多个资源对象,可以使用分号 ; 分隔它们。

那么,它和try-finally语句有什么区别呢?

和try-finally的关系

很明显,try-with-resource语法相对于try-finally语法来说,隐式的调用了资源对象的close方法,语法更简洁,其次,让我们看看如下代码块:

java 复制代码
@Test
public void testException(){

    try{
        throw new CustomException("代码块内的业务异常");
    }finally {
        throw new CustomException("finally中的资源释放异常");
    }
}

运行上面的代码,你将会得到如下异常:

模仿上述代码,看看在try-with-resource语法中又会怎样

先创建一个类MyAutoClose

java 复制代码
@Slf4j
public class MyAutoClose implements AutoCloseable {
    @Override
    public void close() throws Exception {
        log.info("自动关闭");
        throw new CustomException("close方法异常");
    }
}

运行如下单元测试

java 复制代码
@Test
public void testAutoClose() {
    try (MyAutoClose autoClose = new MyAutoClose()) {
        throw new CustomException("业务异常");
    } catch (Exception e) {
        log.error("", e);
    }
}

得到如下异常

综合两种结果来看,可以看到同样的逻辑,业务和finally都是抛出了异常,但是抛出的异常却不一样:在try-finally语句块中,抛出的是finally中的异常,在try-with-resource语句块中,抛出的是业务的异常,而且异常中还携带了close方法关闭时抛出的异常信息。

谁好谁坏,自行体会。

try-with-resources的本质

再进一步细想一下,为啥两种方式逻辑基本一样,但是抛出的异常却不一样?

try-finally代码块很直观,try-with-resource代码块在编译成字节码文件的时候却被编译期"魔改"了,上文提到过

......Java 编译器会在生成的字节码中进行特殊处理。编译器会自动在 try 块结束时插入 finally 块,并在 finally 块中调用 close() 方法来关闭资源。

那只需要通过反编译看下class文件就可以了

再回想一下它原来长什么样子

java 复制代码
@Test
public void testAutoClose() {
    try (MyAutoClose autoClose = new MyAutoClose()) {
        throw new CustomException("业务异常");
    } catch (Exception e) {
        log.error("", e);
    }
}

亲妈都认不出来了。。。

我将反编译后的真正代码分成了四部分,并标记在了图上

  1. 外层异常try-catch块尝试捕获初始化资源异常
  2. 里层try-catch块尝试捕获业务执行异常
  3. 业务try-catch块捕获到异常,直接抛出
  4. finally块判定是否发生了业务异常:如果发生了业务异常,而且close方法执行也发生了异常,则将close方法发生的异常附加业务异常中;如果未发生业务异常,则直接执行close方法,这时候如果发生了异常,直接抛出的就是finally块中的异常了。

总结下,实际上是如下形式

java 复制代码
try{
    //初始化资源
    try{
        //业务代码执行
    }catch(Exceptin e){
        //捕获的业务异常,抛出业务异常
    }finally{
        //执行close方法,并判定在不同情况下的异常信息
    }
}catch(Exception e){
    //捕获的资源初始化异常
}

结论:使用try-with-resource很明显比使用try-finally块要更好一些,它能准确捕获业务异常;但是try-finally块也有不可替代的使用场景,比如资源类未实现AutoCloseable接口的时候

最后,欢迎关注我的博客原文:https://blog.kdyzm.cn/post/179

END.

相关推荐
听风吟丶1 分钟前
Java NIO 深度解析:从核心组件到高并发实战
java·开发语言·jvm
野生技术架构师8 分钟前
Java面试题及答案总结(互联网大厂新版)
java·面试·状态模式
a努力。9 分钟前
小红书Java面试被问:ThreadLocal 内存泄漏问题及解决方案
java·jvm·后端·算法·面试·架构
此生只爱蛋9 分钟前
【Redis】String 字符串
java·数据库·redis
C++业余爱好者10 分钟前
Java开发中Entity、VO、DTO、Form对象详解
java·开发语言
超级大只老咪16 分钟前
“和”与“或”逻辑判断与条件取反(Java)
java·算法
青云交17 分钟前
Java 大视界 -- 基于 Java+Flink 构建实时电商交易风控系统实战(436)
java·redis·flink·规则引擎·drools·实时风控·电商交易
程序员卷卷狗25 分钟前
Java 单例模式的五种实现:饿汉式、懒汉式、DCL、静态内部类、枚举单例
java·开发语言·单例模式
@淡 定26 分钟前
动态代理(JDK动态代理/CGLIB动态代理
java·开发语言·python
悟能不能悟34 分钟前
java 判断string[]中是否有a
java·开发语言