Java 7+ 的 multi-catch 语法用 | 分隔互不相关的并列异常类型(如 IOException | SQLException),要求它们是同一父类的直接子类、不能存在继承关系,捕获变量为 final,不可用 instanceof 判断具体类型。
Java 7 引入了 catch 块中用 | 分隔多个异常类型的写法,前提是这些异常必须是互不相关的(不能是父子类关系),且都继承自同一个父异常(通常是 Exception 或 Throwable)。
正确写法示例:
try {
// 可能抛出 IOException 或 SQLException 的代码
} catch (IOException | SQLException e) {
logger.error("I/O or DB error occurred", e);
}
错误写法(编译不通过):
catch (Exception | RuntimeException e) { ... } // ❌ RuntimeException 是 Exception 子类
IOException 和 SQLException 都是 Exception 的直接子类,满足“并列兄弟”条件e 是 final 的,不可重新赋值catch 中用 instanceof 判断具体类型——因为编译器已将其视为共同父类型(如 Exception)编译器会报错:Alternative catch not disjoint。这是因为 RuntimeException 是 Exception 的子类,二者不是“互斥并列”的异常类型,JVM 无法保证执行路径唯一。
等价于写了两段逻辑重叠的 catch:
catch (Exception e) { ... }
catch (RuntimeException e) { ... } // 永远不会执行到这行
catch 块,且注意顺序:子类异常必须在父类之前
Exception 放最前,导致后续所有 catch 都失效共享一个 catch 块意味着你放弃了按异常类型定制处理方式的能力——除非手动用 if (e instanceof XXX) 分支判断,但这违背了 multi-catch 的设计初衷,也削弱了可读性。
SQLException 解析 SQLState,或对 SocketTimeoutException 做重试,而对 FileNotFoundException 直接返回 404没有语法支持,只能靠外层统一捕获 Exception,再用 if 分支识别具体类型:
try {
doSomething();
} catch (Exception e) {
if (e instanceof IOException || e instanceof SQLException) {
handleIoOrDbError(e);
} else {
throw e; // 不匹配的异常重新抛出
}
}
但这样会丢失编译期检查,且容易漏掉新加入的异常类型。
Exceptions.isAnyOf(e, IOException.class, SQLException.class)
e.getClass().isAssignableFrom(XxxException.class) 不可靠,要用 instanceof 或 Class.isInstance()