何时应尝试使用AutoCloseable构造函数进行封装吗?

问题描述

Oracle Java documentation for the then-new try-with-resources显示了所有示例,其中有问题的AutoCloseabletry(...)中构建:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    ...
}

从某种意义上说,这似乎是安全的,即使构造器(不是某个后续事件)已经获取了必须释放的资源,如果遇到异常,它们也会被释放。

但是,例如,https://www.baeldung.com/java-try-with-resources指出,从Java 9开始,我们可以有效地使用在try(...)之前初始化的最终资源:

FileOutputStream fos = new FileOutputStream("output.txt");
try (fos) { 
    // do stuff
}

在类似Java-9之前的版本中,我看到了:

CustomStreamLikeThing connection = new CustomStreamLikeThing("db_like_thing");
// ... do some stuff that may throw a RuntimeException
try (CustomStreamLikeThing connection2=connection) { 
    // do some more stuff
}

这似乎合法,但同时在某种程度上削弱了这一概念:如果CustomStreamLikeThing获得可关闭的资源,则可能不会释放它们。

是否有关于何时将AutoCloseable的构造函数括在try(...)中的准则,或者这是一个品味问题(例如,“封装所有我认为可能引发异常的东西”) “)?

解决方法

如果可以的话,应该始终使用尝试资源

分配资源时,应确保在使用完资源后始终将其释放。通常,“始终”意味着您应该使用try-finally,以便分配资源后发生的任何错误都不会阻止资源的释放。

这意味着如果您不使用 try-with-resources ,则需要使用try-finally,为什么我要说总是使用try-with-resources?

因为调用close()也会失败,并且 try-with-resources 为您处理此问题,并在 try-with-resources 被添加时添加了一项功能。添加了禁止的异常

如果您曾经在失败的close()调用的堆栈跟踪中被诅咒过,那么您就会知道这一点的重要性,该调用已替换了本应放在 try-中的代码所引发的异常- with-resources 块,因此您现在不知道是什么真正导致了问题。

抑制的异常是一个通常被忽略的功能,但是当close方法抛出级联异常时,它可以节省大量时间。为此,请始终使用 try-with-resources

附带好处:尝试使用资源的代码少于使用try-finally的代码,并且在处理资源时,您应该一定使用这两者之一。 / p>

,

是否有关于何时应该在try(...)中包含AutoCloseable的构造函数的准则,或者这是一个品味问题(例如“封装所有我认为可能引发异常的东西”)? / p>

编码的基本规则是:您故意进行任何操作。这就要求您了解您正在做什么 以及代码中每个“事情”都在做什么。

含义:如果您需要“关闭”某些东西,则常识性规则是:对资源使用try。

但是您要特别询问“对象创建”的微妙方面。应该把它放到try()中还是可以做到?!

为此:没有硬性规定,只要您的源代码合理地是正确的。因此,可以归结为以下两个方面:

  • 您与您的团队交谈,并且您同意您的“首选”风格,然后整个团队都遵循。换句话说:选择您喜欢的样式,然后坚持:保持一致!
  • 您在看您的要求。您的try(fos)选项可用于java9。对于不必修补产品的人们,这很好。但是对于经常需要将“相同更改”修补到同一产品的不同版本的人们来说……这些人可能更喜欢可以仅合并到java8环境中的源代码。
,

java 9之前的版本只是为了允许自动关闭功能在基础对象上工作并关闭和释放资源而设计的

在Java 9中,它更多地嵌入在语言本身中,以允许关闭在try with块之外初始化的资源。

就喜好而言,更多的是关于当您尝试使用块时是否优雅,如果块太大,不妨将其移出。

,

直接在try-with-resources语句中使用构造函数时,如下所示:

try (MyCloseable c = new MyCloseable()) {
    // do stuff with c
} catch (IOException e) {
    // handle exception
}

然后,当以下任何一个抛出异常时,将调用catch块:

  • 构造函数
  • 试身
  • close()方法。

因此,例如都抛出IOException

使用这样的代码,您可以控制异常处理:

MyCloseable c;
try {
    c = new MyCloseable();
} catch (IOException e) {
    // handle constructor exception
}
try (c) {
    // do stuff with c
} catch (IOException e) {
    // handle exception from close() or try-body
}

但是再说一次,在上面的示例中,您仍然为try-body和close方法保留了一个catch块,因此不建议这样做:

// initialize c like in the second example
try (c) {
   try {
       // do stuff with c
   } catch (IOException e) {
       // handle try-body exception
   }
} catch (IOException e) {
   // handle close() exception
}