微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

尝试使用资源的文件编写器是一种好习惯

如何解决尝试使用资源的文件编写器是一种好习惯

我在网上和《有效Java》一书(约书亚·布洛赫(Joshua Bloch))中看到了这个示例。

try(BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))){
  writer.write(str); // do something with the file we've opened
}
catch(IOException e){
  // handle the exception
}

此示例没有问题,BufferedWriter自动关闭,然后关闭FileWriter;但是,在其他情况下,如果我们以这种方式声明2个嵌套资源:

try (AutoClosable res = new Impl2(new Impl1())) {... }

我猜测new Impl1()可能会正常运行,但new Impl2()会崩溃,在这种情况下,Java不会引用Impl1关闭它。

像这样总是总是声明多个资源(即使在这种情况下不是必需的)不是更好的做法吗?

try(FileWriter fw = new FileWriter(fileName); 
    BufferedWriter writer = new BufferedWriter(fw)){ ... }

解决方法

经过一些搜索,我找到了这篇文章:https://dzone.com/articles/carefully-specify-multiple-resources-in-single-try

根据定义JLS 14.20.3ResourceListResource隔开的;组成。基于此,我们可以得出结论,像AutoClosable res = new Impl2(new Impl1())这样的嵌套初始化是单个资源。由于为try-with-resources定义了具有多种资源的规则,因此此处不适用,重要的规则是:

资源按从左到右的顺序初始化。如果资源未能初始化(即,其初始化程序表达式引发异常),则将关闭迄今为止使用try-with-resources语句初始化的所有资源。如果所有资源都成功初始化,则try块将正常执行,然后将关闭try-with-resources语句的所有非空资源。

以与初始化资源相反的顺序关闭资源。仅当资源初始化为非空值时,它才关闭。一个资源关闭的异常不会阻止其他资源的关闭。如果初始化程序,try块或资源关闭之前引发了异常,则可以抑制这种异常。

此外,Implt1#close()除非在Impl2#close()内部被显式调用,否则不会被调用

简而言之,最好在用;分隔的单独语句中声明多个资源,如下所示:

try(Impl1 impl1 = new Impl1(); 
    Impl2 impl2 = new Impl2(impl1))
,

如果try部分中发生异常,则不存在资源泄漏(假设Impl1编写正确)。请注意,Impl1()中引发的异常不会到达Impl2,因为在调用构造函数参数之前对其进行了评估。

try (AutoClosable res = new Impl2(new Impl1())) {

因此可以嵌套此类包装构造函数;如果代码不会太长,则样式会更好。

备注:FileWriterFileReader是使用平台编码的旧实用程序类,根据应用程序的安装而有所不同。

Path path = Paths.get(fileName);
try (BufferedWriter writer =
        Files.newBufferedWriter​(path,StandardCharsets.UTF8)) {
,

首先,为了进行一点理智检查,我想说的是,您找不到约书亚·布洛赫(Joshua Bloch)的Effective Java(2018年第三版)中提供的示例。如果您正在阅读以前的版本,则最好获得最新的版本。如果这很不好,请转至该页面。


现在关于问题本身。

让我们从已经提到的JSL §14.20.3开始,它说资源由 ;分隔。这明确意味着,不管对象创建的装饰链有多长(例如new Obj1(new Obj2(...new ObjK(..)));,它都将被视为 one 单一资源,因为它是一个声明。

现在我们知道单一资源的构成,让我从观察中得到一些启示:

原因,为什么最好单独定义资源:

  1. JSL §14.20.3还指出:

如果资源未能初始化(即,其初始化程序表达式引发异常),则将关闭到目前为止由try-with-resources语句初始化的所有资源。如果所有资源都成功初始化,则try块将正常执行,然后将关闭try-with-resources语句的所有非空资源。

:这对我们意味着什么?:

A :如果单个资源是传递给包装构造函数的对象链,则不是的意思是,抛出初始化阶段将关闭那些嵌入的资源,而不是在 parent (包装器,封装对象)上调用.close()方法,因此,您很容易以资源泄漏告终。

另一方面,如果您已定义separately,则可以放心所有资源将被关闭。

  1. JSL §14.20.3还指出:

一个资源关闭的异常不会阻止其他资源的关闭。如果初始化程序,try块或资源关闭之前引发了异常,则可以抑制这种异常。

:这对我们意味着什么?

A :如果您分别声明了资源,则哪个引发异常和哪个不引发异常都没有关系。它们都将成功关闭。

  1. 很不幸,您提到的书(至少是3rd version )没有涵盖您在此处提出的问题;但是,我进行了更多研究,发现Cay S. Horstmann的Core Java(第10版)在其§7.2.5中证实了我上面提到的观点:

当块正常退出或出现异常时,in.close() 方法被调用,就像使用了finally块一样。

无论块如何退出,inout都是关闭的。

在本书中给出的示例中,

inout是可自动关闭的对象。

:这是什么意思?

A :这意味着,从一种资源的任何阶段引发的异常不会以任何方式影响关闭另一种资源的方式。


基于上述所有内容,我认为,这还取决于资源的实现方式。例如。如果封闭资源在调用其.close()时实现了关闭封闭资源的逻辑,那么您所关注的是:

我猜可能会发生新的Impl1()正常运行,但新的Impl2()崩溃的情况,在这种情况下,Java将无法引用Impl1来关闭它。

在您的特定情况下并不是真正的问题,因为正在关闭的容器资源将持有对所包含资源的引用,并最终将其关闭(或仅实现其关闭 );

但是,由于我提出的几个原因,在将资源链接到包装构造函数的过程中构造资源仍然不是一个好主意。


最后还是PS

在任何情况下,都无需考虑资源的实现方式如何链接资源等。所有内容,包括{{3} },JLS本书,Core Java本书以及此处提到的要点表明,始终最好分别声明 声明您的资源。


版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。