Java 在一个try语句中打开多个资源是否不可靠?为什么?
该计划:Java 在一个try语句中打开多个资源是否不可靠?为什么?,java,try-with-resources,Java,Try With Resources,该计划: import java.util.*; public class Main { static Set<String> openResources = new TreeSet<>(); static class MyResource implements AutoCloseable { boolean close; MyResource encapsulatedResource; String
import java.util.*;
public class Main {
static Set<String> openResources = new TreeSet<>();
static class MyResource implements AutoCloseable {
boolean close;
MyResource encapsulatedResource;
String name;
MyResource(String id, boolean exceptionOnCreate, boolean exceptionOnClose, MyResource encapsulatedResource) {
this.close = exceptionOnClose;
this.encapsulatedResource = encapsulatedResource;
this.name = id;
if (exceptionOnCreate) {
throw new RuntimeException("Exception when creating " + id);
}
openResources.add(id);
System.out.println(name + " is now open");
}
@Override
public void close() {
if (close) {
throw new RuntimeException("Exception when closing " + name);
}
if (encapsulatedResource != null) {
encapsulatedResource.close();
}
openResources.remove(name);
System.out.println(name + " was successfully closed");
}
}
public static void main(String[] args) {
try (AutoCloseable resource1 = new MyResource("resource1", false, false, null);
MyResource resource2 = new MyResource("resource2", false, true, null);
AutoCloseable resource3 = new MyResource("resource3", false, false, resource2);
AutoCloseable resource4 = new MyResource("resource4", true, false, null);) {
System.out.println("main program");
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("openResources: " + openResources);
}
}
我希望在使用try-with-resources时,所有资源都已正确关闭,并且不会有任何泄漏。然而,在这里,资源2和资源3被泄漏了。为什么?我不能依赖这种语法吗?参考资料2和3“泄漏”了,因为您定义异常的方式不同。由于资源4在创建时抛出异常,因此创建的资源将被关闭。资源3将尝试关闭,但由于它实际上正在尝试关闭资源2,因此资源3将引发异常(注意:此异常被抑制,因为它是在try部分中为原始异常提供服务时引发的)。关闭资源2还将导致抑制的异常,使资源1只剩下关闭的资源。根据:
如果初始化资源的一次自动关闭突然完成
因为V值的抛出,以及所有其他自动关闭
初始化的资源正常完成,然后使用资源重试
语句由于抛出值V而突然完成
所以,这就是设计。引入try-with-resources的原因是为了减少开发人员的任务并自行处理资源处理。就可靠性而言,即使是旧的代码方式也会导致相同的资源泄漏。让我们试着用下面JAVA文档中提到的方法翻译代码:
让我们通过重新修改您的代码,并用在try中创建资源的旧方法编写代码,并最终完成,来尝试理解这一点
public class Main {
static Set<String> openResources = new TreeSet<>();
static class MyResource implements AutoCloseable {
boolean close;
MyResource encapsulatedResource;
String name;
MyResource(String id, boolean exceptionOnCreate, boolean exceptionOnClose, MyResource encapsulatedResource) {
this.close = exceptionOnClose;
this.encapsulatedResource = encapsulatedResource;
this.name = id;
if (exceptionOnCreate) {
throw new RuntimeException("Exception when creating " + id);
}
openResources.add(id);
System.out.println(name + " is now open");
}
@Override
public void close() {
if (close) {
throw new RuntimeException("Exception when closing " + name);
}
if (encapsulatedResource != null) {
encapsulatedResource.close();
}
openResources.remove(name);
System.out.println(name + " was successfully closed");
}
}
public static void main(String[] args) {
AutoCloseable resource1 = null, resource3 = null, resource4 = null;
MyResource resource2 = null;
try {
resource1 = new MyResource("resource1", false, false, null);
resource2 = new MyResource("resource2", false, true, null);
resource3 = new MyResource("resource3", false, false, resource2);
resource4 = new MyResource("resource4", true, false, null);
System.out.println("main program");
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
resource1.close();
resource2.close();
resource3.close();
resource4.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
System.out.println("openResources: " + openResources);
}
}
如您所见,资源2和资源3是泄漏的资源
关闭资源2会导致异常,从而阻止关闭
资源3
类似地,对于try with resources,它希望在关闭时不会抛出异常,如果抛出异常,资源的关闭将在该时间点终止
这并不意味着try-with-resources不可靠或过时
资源处理更好。这只是意味着可关闭的
资源工作不正常,应已关闭或至少关闭
应该已经处理了异常,以便剩余的资源可以关闭
继续
希望这能有所帮助。这与try-catch的“可靠性”无关,而与您连接资源的方式以及它们抛出异常的事实有关。试图关闭封装的resource2时,关闭resource3将引发异常。关闭resource2将引发异常。是的,您创建的示例确实存在上述缺陷。您已经发布了翻译,但您的代码不是等效的。@SotiriosDelimanolis感谢您的评论。我已经发布了JAVA文档所说的内容,但是如果您觉得它不同,请分享上述代码的等效内容。除此之外,如果ResourceSpecification声明了n>1个资源,那么ResourceSpecification_____________________________________,以相同的顺序(try-catch-finally语句本身就是try-with-resources语句)。每个
close()
调用都包装在一个null
检查和它自己的try-catch
语句中。它对所有资源调用close
。在您的代码中,它将在第一次调用失败后停止。@SotiriosDelimanolis根据JAVA文档:如果一个初始化资源的自动关闭由于抛出值V而突然完成,而所有其他初始化资源的自动关闭正常完成,然后try with resources语句突然完成,因为抛出了值V。这导致了所有关闭都在一个try-catch-only中的情况。它特别指出[…]和所有其他自动关闭[…]。例如,如果第一个关闭失败,您的代码不会执行所有其他关闭。这句话的意思是,如果其中只有一个失败,那么相应的异常就是try with resources语句抛出的异常。如果不止一个抛出一个异常,那么就会应用整个被抑制的异常行为。
public class Main {
static Set<String> openResources = new TreeSet<>();
static class MyResource implements AutoCloseable {
boolean close;
MyResource encapsulatedResource;
String name;
MyResource(String id, boolean exceptionOnCreate, boolean exceptionOnClose, MyResource encapsulatedResource) {
this.close = exceptionOnClose;
this.encapsulatedResource = encapsulatedResource;
this.name = id;
if (exceptionOnCreate) {
throw new RuntimeException("Exception when creating " + id);
}
openResources.add(id);
System.out.println(name + " is now open");
}
@Override
public void close() {
if (close) {
throw new RuntimeException("Exception when closing " + name);
}
if (encapsulatedResource != null) {
encapsulatedResource.close();
}
openResources.remove(name);
System.out.println(name + " was successfully closed");
}
}
public static void main(String[] args) {
AutoCloseable resource1 = null, resource3 = null, resource4 = null;
MyResource resource2 = null;
try {
resource1 = new MyResource("resource1", false, false, null);
resource2 = new MyResource("resource2", false, true, null);
resource3 = new MyResource("resource3", false, false, resource2);
resource4 = new MyResource("resource4", true, false, null);
System.out.println("main program");
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
resource1.close();
resource2.close();
resource3.close();
resource4.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
System.out.println("openResources: " + openResources);
}
}
resource1 is now open
resource2 is now open
resource3 is now open
Exception when creating resource4
resource1 was successfully closed
Exception when closing resource2
openResources: [resource2, resource3]