Java 尝试使用文件编写器是一种好的做法吗

Java 尝试使用文件编写器是一种好的做法吗,java,java-7,try-with-resources,Java,Java 7,Try With Resources,我在网上和Joshua Bloch的《高效Java》一书中看到了这个例子 这个例子没有问题,BufferedWriter,它将自动关闭,然后关闭FileWriter;但是,在另一种情况下,如果我们以这种方式声明2个嵌套资源: try (AutoClosable res = new Impl2(new Impl1())) {... } 我想可能会发生new Impl1()执行得很好的情况,但是new Impl2()崩溃,在这种情况下,为了关闭它,Java不会引用Impl1 像这样始终独立声明多个

我在网上和Joshua Bloch的《高效Java》一书中看到了这个例子

这个例子没有问题,
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)){ ... }

经过一番搜索,我找到了这篇文章:

根据定义,
资源列表由
资源
组成,由
分隔。基于此,我们可以得出这样的结论:嵌套初始化(如
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())) {
因此,嵌套这样的包装构造函数是很好的;如果代码不会变得太长,那么样式会更好

一句话:
FileWriter
FileReader
是使用平台编码的旧实用程序类,每个应用程序安装都会有所不同

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

首先,为了进行一点理智检查,我想说,我无法在Joshua Bloch(2018年第三版)中找到您提供的示例。如果您正在阅读以前的版本,最好是获取最新版本。如果是我的错,请把具体的页码告诉我


现在关于问题本身

让我们从开始,也就是说,资源由
分隔。这意味着,不管对象创建的装饰链有多长(例如,
newobj1(newobj2(…newobjk(…));
它将被视为一个单一资源,因为它是一个定义/语句

既然我们现在知道单一资源构成什么,让我从我的观察中阐明一些道理

原因,为什么单独定义资源更好
  • 还声明:
  • 如果资源未能初始化(即,其初始值设定项表达式引发异常),则关闭到目前为止由try with resources语句初始化的所有资源。如果所有资源初始化成功,try块将正常执行,然后关闭try with resources语句的所有非空资源

    Q:这对我们意味着什么

    A:如果单个资源是传递给包装构造函数的对象链,则它并不意味着从初始化阶段抛出异常将关闭这些嵌入的资源,而将在父级(包装,封闭)上调用
    .close()
    方法对象,因此,很容易导致资源泄漏

    另一方面,您可以放心,如果您已经定义了所有资源,那么它们都将被关闭

  • 还声明:
  • 关闭一个资源时出现的异常不会阻止关闭其他资源。如果初始值设定项、try块或资源关闭之前引发了异常,则会抑制此类异常

    Q:这对我们意味着什么

    A:如果您单独声明了资源,那么哪个引发异常,哪个不引发异常都无关紧要。它们都将成功关闭

  • 不幸的是,你提到的那本书(至少是it)没有涵盖你在这里提出的问题;然而,我做了更多的研究,发现Cay S.Horstmann(第10版)证实了我在其§7.2.5中提到的观点:
  • 当块正常退出或出现异常时,.close()中的
    
    方法被调用,就像您使用了finally块一样

    无论块如何退出,
    in
    out
    都将关闭

    在本书给出的示例中,
    in
    out
    是可自动关闭的对象

    Q:这是什么意思

    A:这意味着从一个资源的任何阶段抛出的异常无论如何都不会影响另一个资源的关闭方式


    基于以上所述,我认为这也取决于资源是如何实现的。例如,如果所包含的资源
    Path path = Paths.get(fileName);
    try (BufferedWriter writer =
            Files.newBufferedWriter​(path, StandardCharsets.UTF8)) {