应该在什么时候使用 try-with 块来包含 AutoCloseable 构造函数?

huangapple go评论136阅读模式
英文:

(When) Should try-with enclose the AutoCloseable constructor?

问题

Oracle Java documentation for the then-new try-with-resources中的所有示例都显示了在try(...)中构造了相关的AutoCloseable

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) { 
    // 做一些操作
}

在类似的Java 9之前的情况下,我也看到过:

CustomStreamLikeThing connection = new CustomStreamLikeThing("db_like_thing");
// ... 做一些可能抛出RuntimeException的操作
try (CustomStreamLikeThing connection2 = connection) { 
    // 做一些其他操作
}

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

关于何时应该将AutoCloseable的构造函数包含在try(...)中,是否有指导方针,还是这是一种品味问题(就像“将我认为可能会抛出异常的所有内容都包括进来”)?

英文:

The Oracle Java documentation for the then-new try-with-resources shows all examples with the AutoCloseable in question being constructed in the try(...):

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

That seems secure in the sense that even if the constructor (as opposed to some subsequent event) already acquires resources that have to be released, they would be released if we ran into an exception.

However, as, e.g., https://www.baeldung.com/java-try-with-resources points out, starting with Java 9, we can use effectively final resources initialized before the try(...):

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

In a similar, pre-Java-9 vein, I have seen:

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

Which seems legal, but at the same time to somewhat weaken the concept: If the CustomStreamLikeThing acquires closeable resources, they may not be released.

Are there guidelines about when the constructor of an AutoCloseable should be enclosed in the try(...), or is this a matter of taste (as in "enclose all those things that I think could throw an exception")?

答案1

得分: 2

你应该总是在可能的情况下使用try-with-resources

当你分配资源时,确保在完成后始终释放该资源。这里的“始终”通常意味着你应该使用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-with-resources比使用try-finally需要更少的代码,而当处理资源时,你应该绝对使用这两者之一。

英文:

You should always use try-with-resources if you can.

When you allocate a resource, you should make sure the resource is always released when you're done with it. That "always" generally means that you should use a try-finally, so that any error occurring after the resource is allocate won't prevent the resource from being released.

Which means that if you don't use try-with-resources, you need to use try-finally, so why did I say to always use try-with-resources?

Because calling close() can fail too, and try-with-resources handles that for you, with a feature added when try-with-resources was added, called suppressed exceptions.

You'll know the importance of this if you've ever cursed at a stacktrace of a failed close() call, that has replaced the exception thrown by the code that should have been in a try-with-resources block, so you now don't know what actually caused the problem.

Suppressed exceptions is a commonly overlooked feature, but is a great time saver when a cascaded exception is thrown by a close method. For that alone, always use try-with-resources.

Side benefit: Try-with-resources is less code than using try-finally, and you should definitely use one of those two when handling resources.

答案2

得分: 1

当构造函数直接位于try-with-resources语句中时,像这样:

try (MyCloseable c = new MyCloseable()) {
    // 使用c进行操作
} catch (IOException e) {
    // 处理异常
}

那么在以下任何情况下抛出异常时,都将调用catch块:

  • 构造函数
  • try块体
  • close()方法

因此,你不能为每个特殊情况都有一个捕获块,例如所有情况都抛出IOException

而使用以下代码,你可以对异常处理进行更精细的控制:

MyCloseable c;
try {
    c = new MyCloseable();
} catch (IOException e) {
    // 处理构造函数异常
}
try (c) {
    // 使用c进行操作
} catch (IOException e) {
    // 处理close()或try块体的异常
}

但同样地,在上面的示例中,try块体和close方法仍然有一个单独的捕获块,像这样做是不推荐的:

// 像第二个示例中那样初始化c
try (c) {
   try {
       // 使用c进行操作
   } catch (IOException e) {
       // 处理try块体异常
   }
} catch (IOException e) {
   // 处理close()异常
}
英文:

When having the constructor directly in the try-with-resources statement like this:

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

then the catch block will be called when any of the following throw an exception:

  • the constructor
  • the try-body
  • the close()-method.

So you can't have a catch block for every special case when e.g. all throw an IOException.

Whereas with code like this, you can control the exception handling a bit more:

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
}

But then again, you still have a single catch block for the try-body and the close method in the above example, and doing something like this would not be recommended:

// 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
}

答案3

得分: 1

> 关于实现了AutoCloseable接口的构造函数何时应该被包含在try(...)中,是否这是一种品味问题(就像“将我认为可能会抛出异常的所有内容都包含进去”)有相关的指南吗?

编码的基本规则是:你有意识地进行任何操作。这要求你理解你在做什么,以及代码中的每个“事物”正在做什么。

意味着:如果有需要被“关闭”的东西,那么常识性的规则是:使用带资源的try。

但你特别询问了关于“对象创建”的微妙方面。它应该放在try()中,还是可以在之前完成?

对此:没有硬性规定,只要你的源代码在语义上做正确的事情。因此,它归结为这两个方面:

  • 与你的团队进行沟通,就你们“偏好”的风格达成一致,然后整个团队都遵循这个风格。换句话说:选择你喜欢的风格,然后坚持下去:保持一致!
  • 你要看你的需求。你的try(fos)选项适用于Java 9。对于那些不必修补产品的人来说,这是可以的。但对于那些经常需要将“相同更改”打补丁到同一产品的不同版本的人来说...这些人可能更喜欢源代码,这些代码可以合并到Java 8环境中。
英文:

> Are there guidelines about when the constructor of an AutoCloseable should be enclosed in the try(...), or is this a matter of taste (as in "enclose all those things that I think could throw an exception")?

The basic rule in coding is: you do anything on purpose. Which requires that you understand what you are doing, and what each "thing" in your code is doing.

Meaning: if you have something that needs to be "closed", then the common sense rule is: use try with resources.

But you are asking specifically about the subtle aspect of the "object creation". Should that go into the try(), or can it be done before?!

For that: no hard rules, as long as your source code does semantically the right thing. Therefore, it boils down to these two aspects:

  • You talk to your team, and you agree on your "preferred" style, and then your whole team follows that. In other words: pick the style you prefer, and then stick to that: be consistent!
  • You look at your requirements. Your try(fos) option works with java9. For people that don't have to patch older products, that is fine. But for people that regularly have to patch "the same change" to different versions of the same product ... those people might prefer source code that can just be merged into a java8 environment.

答案4

得分: 0

pre-java 9 is just hack to allow autoclosable to work on the underlying object and close and release resources.

With java 9, it's more embedded in the language itself to allow closing of resources initialized outside the try with block.

In terms of preference, it's more about whether it's elegant by the time you finish the try with block, if it becomes too huge, might as well move it out.

英文:

The pre-java 9 is just hack to allow autoclosable to work on the underlying object and close and release resources.

With java 9, it's more embedded in the language itself to allow closing of resources initialized outside the try with block.

In terms of preference, it's more about whether its elegant by the time you finish the try with block, if it becomes too huge, might as well move it out.

huangapple
  • 本文由 发表于 2020年9月8日 19:47:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/63793265.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定