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.

相关推荐
Cynthia-石头10 分钟前
docker镜像下载到本地,并导入服务器
java·开发语言·eureka
Seven9718 分钟前
算法题:数组中的第k个最大元素
java·leetcode
huangyujun992012330 分钟前
设计模式杂谈-模板设计模式
java·设计模式
残*影39 分钟前
Spring 中注入 Bean 有几种方式?
java·后端·spring
magic 2451 小时前
Java设计模式:责任链模式
java·设计模式·责任链模式
我是苏苏1 小时前
C#基础:使用线程池执行并行任务
java·服务器·c#
风象南2 小时前
SpringBoot实现实时弹幕
java·spring boot·后端
编程、小哥哥3 小时前
互联网大厂Java求职面试实战:Spring Boot微服务架构及Kafka消息处理示例解析
java·spring boot·微服务·kafka·面试技巧
magic 2454 小时前
return this;返回的是谁
java·开发语言
sg_knight5 小时前
Eureka 高可用集群搭建实战:服务注册与发现的底层原理与避坑指南
java·spring boot·spring·spring cloud·微服务·云原生·eureka