真的很难抓住那些。因此,强烈建议确保静态初始化器不会抛出任何人可能想要捕获的东西。 (例如,抛出 OutOfMemoryError 很好,不太可能有人想要编写代码来捕获这个并执行替代路径或尝试解决问题)。
这通常从用方法调用替换静态初始化程序开始。替换:
static int a; static {a = 1/0; }
与:
static int a = calculateA();
private static int calculateA() {
return 1/0;
}
当然,这只是沿途的一步。将初始化代码(calculateA 方法)移至单独的类,现在您可以自行测试它,甚至不会遇到捕获类静态初始化期间抛出的异常的问题。
一旦你解决了这个问题,你就可以使用这个“技巧”来解决问题。想象一下,这个类中的 2 个方法需要 a 的值。然后,“推迟”异常:
public class Example {
private static final int a;
private static final Throwable aProblem;
static {
int a = 0;
Throwable aProblem = null;
try {
a = calculateA();
} catch (RuntimeException e) {
aProblem = e;
}
Example.a = a;
Example.aProblem = aProblem;
}
private static int calculateA() { return 1/0; }
public static void apiMethodUsingA1() {
if (aProblem != null) throw aProblem;
return a;
}
public static void apiMethodUsingA2() {
if (aProblem != null) throw aProblem;
return a + 5;
}
}
如果这些选项都不可用,例如因为 A 不是由您编写且无法更改,那么您必须将 A 类委托为“糟糕的 API / 糟糕的库”,并且当您执行面对这样一个库:解决它,接受您需要编写难以维护/丑陋的代码,如果它真的很糟糕,请编写一个包装器来隔离问题。甚至可以使用反射。
这是将异常隔离到代码块中的一种有保证的方法:
package com.foo;
class Example {
static int a = 1/0;
}
class Main {
public static void main(String[] args) throws Exception {
try {
Class<?> c = Class.forName("com.foo.Example");
} catch (ExceptionInInitializerError e) {
System.err.println("Hey I caught it");
Throwable actualException = e.getCause();
// do something with it here
actualException.printStackTrace(); // not this - this is for debugging only!
}
}
}