捕获多个异常时的特定和相同操作

Specific and same actions when catching multiple exceptions

我想以不同的方式处理两种不同类型的异常,然后对两种异常类型进行一些相同的操作。如何在 Java 中做到这一点?

下面的代码显示了我想做的事情,但它不正确,因为一个异常不能被捕获两次。

正确的语法是什么?

try {
    // do something...
} 
catch (ExceptionA e) {
    // actions for ExceptionA
}
catch (ExceptionB e) {
    // actions for ExceptionB
}
catch (ExceptionA | ExceptionB e) {
    // actions for ExceptionA & ExceptionB
}

如果您正在寻找 (ExceptionA | ExceptionB),您将能够抓住任何一个并以相同的方式处理,只需要一个失败。对于多个 Exception 条件,这基本上只是 new (1.7) shorthand。

没有catch(ExceptionA && ExceptionB)

例如:

readFile {
    try {
        open the file;
        determine its size;
        determine its content;
    } catch (fileOpenFailed) {
       doSomething;
    } catch (sizeDeterminationFailed) {
        doSomething;
    } catch (contentDeterminationFailed) {
        doSomething;
    }
}

如果您想将 contentDeterminationFailedsizeDeterminationFailed 作为单个 exception 捕获,那么您可能希望创建自己的自定义 exception 类型,它可以被唯一处理。

使用catch (ExceptionA | ExceptionB e)结构。在 catch 块中,首先执行 instanceof 检查 e 并分别处理异常类型。在此之后,对两种类型进行通用处理。这样你就可以在一个 catch 块中完成所有事情:

try {
    // do something...
} catch (ExceptionA | ExceptionB e) {
    if (e instanceof ExceptionA) {
        // handling for ExceptionA
    } else {
        // handling for ExceptionB
    }
    // common handling for both exception types
}

使用通用代码的方法。

try {
    // do something...
} 
catch (ExceptionA e) {
    // actions for ExceptionA
    doCommon(parameters);
}
catch (ExceptionB e) {
    // actions for ExceptionA
    doCommon(parameters);
}

.....

void doCommon( parameters ) {
  // actions for ExceptionA & ExceptionB
}

这适用于大多数情况。
尽管有一些例外,例如 return。为此,您可以 doCommon return 来电者是否必须 return 并将其用作:

catch (ExceptionA e) {
    // actions for ExceptionA
    if ( doCommon(parameters) )
      return;
}
catch (ExceptionB e) {
    // actions for ExceptionA
    if ( doCommon(parameters) )
      return;
}

"native Java" 解决方案不存在。 JLS 指定(强调我的):

14.20.1. Execution of try-catch

A try statement without a finally block is executed by first executing the try block. Then there is a choice:

If execution of the try block completes normally, then no further action is taken and the try statement completes normally.

If execution of the try block completes abruptly because of a throw of a value V, then there is a choice:

If the run-time type of V is assignment compatible with (§5.2) a catchable exception class of any catch clause of the try statement, then the first (leftmost) such catch clause is selected. The value V is assigned to the parameter of the selected catch clause, and the Block of that catch clause is executed, and then there is a choice:

If that block completes normally, then the try statement completes normally.

If that block completes abruptly for any reason, then the try statement completes abruptly for the same reason.

If the run-time type of V is not assignment compatible with a catchable exception class of any catch clause of the try statement, then the try statement completes abruptly because of a throw of the value V.

所以只有第一个应用的catch块被执行。无法为同一个 try 语句执行两个 catch 块。

您可以重新抛出异常并重新处理:

try {
    try {
        // do something
    } catch( ExceptionA e) {
        // actions for ExceptionA
        throw e;
    } catch( ExceptionB e) {
        // actions for ExceptionB
        throw e;
    } 
} catch( ExceptionA | ExceptionB e) {
    // Actions for exceptions A or B
}

或许,为了清楚起见,您可以重构为方法。我假设异常是已检查的异常。

private void doSomethingHelper() throws ExceptionA, ExceptionB {
    try {
        // do something
     } catch( ExceptionA e) {
         // actions for ExceptionA
         throw e;
     } catch( ExceptionB e) {
         // actions for ExceptionB
         throw e;
     } 
}

public void doSomething() {
    try {
        doSomethingHelper();
    } catch( ExceptionA | ExceptionB e ) {
        // Actions for exceptions A or B
    }
}

这种类型的解决方案是否适合您:

try {
// do something...
} catch (ExceptionA | ExceptionB e) {
    // common code to be called for both...
    // ....
    handleException(e)
}

//.... 
void handleException(ExceptionA e) {
   // code for A
}
//....
void handleException(ExceptionB e) {
   // code for B
}

通过这种方式,您首先对两个问题执行共同的操作,然后在适当的方法中获得每个异常的共同代码。这样你就不需要使用 instanceof 或者 ifs.

另一种仅使用 java 构造的方法是将 finally 也放入:

boolean fail = false;

try {
  // your code
} catch (ExceptionA a) {
   // exceptionA specific handling
   fail = true;
} catch (ExceptionB b) {
   // exceptionB specific handling
   fail = true;
} finally {
    if (fail) {
       // common handling
    }
}