Java 中的嵌套 try finally 块、等价性和保证

问题描述 投票:0回答:1

最近我正在研究一种“清理”方法,我想确保尝试执行每个语句(全部或根本不执行)。我的第一个方法是这样的(其中

s#;
是陈述):

try {
    s1;
} finally {
    try {
        s2;
    } finally {
        s3;
    }
}

为了简单起见,我省略了异常处理部分(

catch
块),我在其中存储第一次出现的和随后被抑制的
Throwable
以便稍后重新抛出。

然后我注意到还有另一种(也许更正确?)方法可以做同样的事情:

try {
    try {
        s1;
    } finally {
        s2;
    }
} finally {
    s3;
}

在我看来,第二种方法感觉更正确,因为一旦尝试

s1;
,我们就已经在两个
try
块内,这保证了所有
finally
块都将被执行。

我的问题是:这些方法在 JVM 提供的保证方面实际上有区别吗?

理论上,JVM 在第一个代码示例中的

Error
处抛出
// not a statement
是否有可能(参见下面的代码),从而不再尝试内部
try
块?

try {
    s1;
} finally {
    // not a statement
    try {
        s2;
    } finally {
        s3;
    }
}

第二个例子可以避免此类问题。

java error-handling nested try-catch-finally try-finally
1个回答
0
投票

添加一个

s4
和一个
s5
到混合中,它变得比本来应该的更加清晰,这是一个难以维护的混乱,需要注释来解释为什么你有这种奇怪的嵌套 try 块组合。

因此,我建议您这样做:

Throwable t = null;
try { s1(); } catch (Throwable e) {t = e;}
try { s2(); } catch (Throwable e) {t = e;}
try { s3(); } catch (Throwable e) {t = e;}
if (t != null) throw t;

您可以想象一下,并将

t = e;
替换为实用方法 (
t = update(t, e);
),如果已经发生异常,该方法会将它们附加为抑制的异常,就像 try-finally 的做法一样。

或者,更好的是,添加 lambda 支持:

public final class Cleanup {
  public interface CleanupJob<T extends Throwable> {
    void cleanup() throws T;
  }

  @SafeVarargs
  public static <T extends Throwable> void cleanup(CleanupJob<? extends T>... jobs) {
    Throwable ex = null;
    for (var job : jobs) try {
      job.cleanup();
    } catch (Exception e) {
      if (ex == null) {
        ex = e;
      } else {
        ex.addSuppressed(e);
      }
    }

    if (ex != null) throw (T) ex;
  }
}

使用方法:

import static CleanupRunner.cleanup;

...

cleanup(
() -> s1(),
() -> s2(),
() -> s3(),
() -> s4());

// or even

cleanup(
  this::s1,
  ref::s2,
  SomeClass::staticS3
);

理论上,JVM 在第一个代码示例中是否有可能在 // 不是语句处抛出错误(请参阅下面的代码),从而不再尝试内部 try 块?

不是。

© www.soinside.com 2019 - 2024. All rights reserved.