声明多个有效最终资源时,try-with-resource 不安全吗?
Is try-with-resource not safe when declaring multiple effectively final resources?
从 Java9 开始,我们可以在 try-with-resources 中有效地使用 final 变量。
下面的例子展示了一种资源初始化抛出异常的情况。
public static void main(String[] args) {
Resource1 r1 = new Resource1();
Resource2 r2 = new Resource2(); // exception will be thrown
try (r1; r2) {
System.out.println("TryWithResources.main() try");
} catch (Exception e) {
System.out.println("TryWithResources.main() catch");
}
}
static class Resource1 implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("TryWithResources.Resource1.close()");
}
}
static class Resource2 implements AutoCloseable {
public Resource2() {
throw new RuntimeException();
}
@Override
public void close() throws Exception {
System.out.println("TryWithResources.Resource2.close()");
}
}
当我 运行 这个例子时,我得到的唯一输出是一个 RuntimeException,这意味着 Resource1 没有关闭。这是预料之中的,因为它没有在 try-with-resources 中初始化。
但是,这是预期的结果吗,还是我遗漏了什么?
因为,如果这实际上是它应该工作的方式,那么在我看来,这种新语法实际上消除了很多最初由 try-with-resources 语句带来的安全性。
有人可以确认一下是否真的如此吗?
而且,如果是肯定的,我们为什么要将这种语法用于多种资源并冒这个风险?
我认为您假设“初始化”发生在 try
语句中。构造函数 在 到达 try-with-resources
之前抛出异常。换句话说,行 try (r1; r2) {
本身并没有初始化资源,它只是将它们作为变量引用。它不同于在 try
块中初始化资源:
try (r1; Resource2 r2 = new Resource2()) { // this causes r1 to close if r2 throws an exception
话虽如此,您的观点是正确的,即新语法(访问最终变量)以可能无法关闭先前创建的资源(如您的案例所示)为代价提供了灵活性。就我个人而言,我从来没有理由使用这种新语法。我想不出不在 try
语句中创建资源的充分理由,毕竟在资源关闭后没有必要使用资源。
为了使 try-with-resources
机制起作用,您应该将初始化代码放入 try
。您的初始化似乎发生在 1 行之前。如果你愿意:
public static void main(String[] args) {
try (Resource1 r1 = new Resource1(); Resource2 r2 = new Resource2()) {
System.out.println("TryWithResources.main() try");
} catch (Exception e) {
System.out.println("TryWithResources.main() catch");
}
}
将打印:
TryWithResources.Resource1.close()
TryWithResources.main() catch
所以你的代码应该这样读:“我想自动关闭 r1
和 r2
但我更喜欢自己初始化它”
我的版本应该这样读:“我想自动关闭 r1
和 r2
并且我想将其初始化为 [=13] 的一部分=]块
从 Java9 开始,我们可以在 try-with-resources 中有效地使用 final 变量。
下面的例子展示了一种资源初始化抛出异常的情况。
public static void main(String[] args) {
Resource1 r1 = new Resource1();
Resource2 r2 = new Resource2(); // exception will be thrown
try (r1; r2) {
System.out.println("TryWithResources.main() try");
} catch (Exception e) {
System.out.println("TryWithResources.main() catch");
}
}
static class Resource1 implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("TryWithResources.Resource1.close()");
}
}
static class Resource2 implements AutoCloseable {
public Resource2() {
throw new RuntimeException();
}
@Override
public void close() throws Exception {
System.out.println("TryWithResources.Resource2.close()");
}
}
当我 运行 这个例子时,我得到的唯一输出是一个 RuntimeException,这意味着 Resource1 没有关闭。这是预料之中的,因为它没有在 try-with-resources 中初始化。
但是,这是预期的结果吗,还是我遗漏了什么?
因为,如果这实际上是它应该工作的方式,那么在我看来,这种新语法实际上消除了很多最初由 try-with-resources 语句带来的安全性。
有人可以确认一下是否真的如此吗? 而且,如果是肯定的,我们为什么要将这种语法用于多种资源并冒这个风险?
我认为您假设“初始化”发生在 try
语句中。构造函数 在 到达 try-with-resources
之前抛出异常。换句话说,行 try (r1; r2) {
本身并没有初始化资源,它只是将它们作为变量引用。它不同于在 try
块中初始化资源:
try (r1; Resource2 r2 = new Resource2()) { // this causes r1 to close if r2 throws an exception
话虽如此,您的观点是正确的,即新语法(访问最终变量)以可能无法关闭先前创建的资源(如您的案例所示)为代价提供了灵活性。就我个人而言,我从来没有理由使用这种新语法。我想不出不在 try
语句中创建资源的充分理由,毕竟在资源关闭后没有必要使用资源。
为了使 try-with-resources
机制起作用,您应该将初始化代码放入 try
。您的初始化似乎发生在 1 行之前。如果你愿意:
public static void main(String[] args) {
try (Resource1 r1 = new Resource1(); Resource2 r2 = new Resource2()) {
System.out.println("TryWithResources.main() try");
} catch (Exception e) {
System.out.println("TryWithResources.main() catch");
}
}
将打印:
TryWithResources.Resource1.close()
TryWithResources.main() catch
所以你的代码应该这样读:“我想自动关闭 r1
和 r2
但我更喜欢自己初始化它”
我的版本应该这样读:“我想自动关闭 r1
和 r2
并且我想将其初始化为 [=13] 的一部分=]块