mis*_*tor 128 java exception static-initializer
为什么Java不允许从静态初始化块中抛出已检查的异常?这个设计决定背后的原因是什么?
Kos*_*801 113
因为无法在源中处理这些已检查的异常.您无法控制初始化过程,并且无法从源调用静态{}块,因此您可以使用try-catch包围它们.
因为您无法处理由已检查异常指示的任何错误,所以决定禁止抛出已检查的异常静态块.
静态块不能抛出已检查的异常,但仍允许抛出未经检查的/运行时异常.但根据上述原因,你也无法处理这些问题.
总而言之,这种限制阻止(或者至少使开发人员更难以)构建可能导致应用程序无法恢复的错误的东西.
kev*_*rpe 65
您可以通过捕获任何已检查的异常并将其重新抛出为未经检查的异常来解决此问题.这个未经检查的异常类适用于包装器:java.lang.ExceptionInInitializerError
.
示例代码:
protected static class _YieldCurveConfigHelperSingleton {
public static YieldCurveConfigHelper _staticInstance;
static {
try {
_staticInstance = new YieldCurveConfigHelper();
}
catch (IOException | SAXException | JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
And*_*s_D 20
它必须看起来像这样(这不是有效的Java代码)
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
Run Code Online (Sandbox Code Playgroud)
但广告如何抓住它?已检查的异常需要捕获.想象一下可以初始化类的一些例子(或者可能不是因为它已经被初始化),并且只是为了引起它将引入的复杂性的注意,我将这些例子放在另一个静态的initalizer中:
static {
try {
ClassA a = new ClassA();
Class<ClassB> clazz = Class.forName(ClassB.class);
String something = ClassC.SOME_STATIC_FIELD;
} catch (Exception oops) {
// anybody knows which type might occur?
}
}
Run Code Online (Sandbox Code Playgroud)
另一件令人讨厌的事 -
interface MyInterface {
final static ClassA a = new ClassA();
}
Run Code Online (Sandbox Code Playgroud)
想象一下,ClassA有一个静态初始化器抛出一个检查异常:在这种情况下,MyInterface(它是一个带有'hidden'静态初始化器的接口)必须抛出异常或处理它 - 在接口处进行异常处理?最好保持原样.
为什么Java不允许从静态初始化块中抛出已检查的异常?
从技术上讲,你可以这样做.但是,必须在块内捕获已检查的异常.不允许检查的异常传播出块.
从技术上讲,还可以允许未经检查的异常传播出静态初始化块1.但故意这样做是一个非常糟糕的主意!问题是JVM本身捕获未经检查的异常,并将其包装并重新抛出它作为一个ExceptionInInitializerError
.
注意:这Error
不是一个常规的例外.你不应该试图从中恢复.
在大多数情况下,无法捕获异常:
public class Test {
static {
int i = 1;
if (i == 1) {
throw new RuntimeException("Bang!");
}
}
public static void main(String[] args) {
try {
// stuff
} catch (Throwable ex) {
// This won't be executed.
System.out.println("Caught " + ex);
}
}
}
$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
at Test.<clinit>(Test.java:5)
Run Code Online (Sandbox Code Playgroud)
没有地方你可以放在try ... catch
上面抓住ExceptionInInitializerError
2.
在某些情况下,你可以抓住它.例如,如果通过调用触发了类初始化Class.forName(...)
,则可以将调用括在a中try
并捕获ExceptionInInitializerError
或后续NoClassDefFoundError
.
但是,如果您试图从中恢复,ExceptionInInitializerError
则可能会遇到障碍.问题是在抛出错误之前,JVM将导致问题的类标记为"失败".你根本无法使用它.此外,依赖于失败类的任何其他类如果尝试初始化也将进入失败状态.前进的唯一方法是卸载所有失败的类.这对于动态加载的代码3 可能是可行的,但通常不是.
1 - 如果静态块无条件地抛出未经检查的异常,则会出现编译错误.
2 - 您可以通过注册默认的未捕获异常处理程序来拦截它,但这不允许您恢复,因为您的"主"线程无法启动.
3 - 如果要恢复失败的类,则需要删除加载它们的类加载器.
这个设计决定背后的原因是什么?
它是为了保护程序员不要编写抛出无法处理的异常的代码!
正如我们所看到的,静态初始化程序中的异常将典型应用程序变为砖块.最好的事情是语言设计者可以做的是将已检查的案例作为编译错误来处理.(不幸的是,对于未经检查的例外,这也是不切实际的.)
好的,那么如果您的代码"需要"在静态初始化程序中抛出异常,您应该怎么做.基本上,有两种选择:
如果(完全!)可以从块中的异常中恢复,那么就这样做.
否则,重构代码,以便初始化不会发生在静态初始化块(或静态变量的初始化器)中.
归档时间: |
|
查看次数: |
83228 次 |
最近记录: |