Java为什么不允许从静态初始化块中引发检查异常?这个设计决定背后的原因是什么?
Java为什么不允许从静态初始化块中引发检查异常?这个设计决定背后的原因是什么?
Answers:
因为不可能在源中处理这些检查的异常。您对初始化过程没有任何控制,并且无法从源代码中调用static {}块,因此可以用try-catch包围它们。
因为您无法处理由检查的异常指示的任何错误,所以决定禁止抛出检查的异常静态块。
静态块不得引发已检查的异常,但仍允许引发未检查的/运行时异常。但是根据上述原因,您也将无法处理这些问题。
总而言之,此限制可防止(或至少使开发人员更难)构建可能会导致错误的应用程序无法恢复的东西。
static { if(1 < 10) { throw new NullPointerException(); } }
您可以通过捕获任何已检查的异常并将其重新抛出为未检查的异常来解决此问题。这个未经检查的异常类可以很好地用作包装器: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);
}
}
}
catch (Exception e) {
代替。
System.exit(...)
(或同等学历)是唯一的选择,
它必须看起来像这样(这不是有效的Java代码)
// Not a valid Java Code
static throws SomeCheckedException {
throw new SomeCheckedException();
}
但是在哪里抓广告呢?已检查的异常需要捕获。想象一些可能初始化该类的示例(或者可能不会因为已经初始化了该类),并且只是为了引起人们对其引入的复杂性的注意,我将这些示例放在另一个静态初始化器中:
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?
}
}
还有一件令人讨厌的事情-
interface MyInterface {
final static ClassA a = new ClassA();
}
想象一下ClassA的静态初始化程序抛出了一个已检查的异常:在这种情况下,MyInterface(这是一个带有“隐藏”静态初始化程序的接口)将不得不抛出该异常或对其进行处理-接口处的异常处理?最好保持原样。
main
可以抛出检查异常。显然那些无法处理。
main()
,该异常将带有堆栈跟踪的异常打印到System.err
,然后调用System.exit()
。最后,这个问题的答案可能是:“因为Java设计人员是这样说的”。
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)
您无法try ... catch
在上面放置a 来捕获ExceptionInInitializerError
2。
在某些情况下,您可以抓住它。例如,如果通过调用触发了类初始化Class.forName(...)
,则可以将调用括在中,try
并捕获ExceptionInInitializerError
或后续的NoClassDefFoundError
。
但是,如果您尝试恢复从ExceptionInInitializerError
你容易碰到路障。问题在于,在引发错误之前,JVM将导致问题的类标记为“失败”。您将无法使用它。而且,依赖于失败类的任何其他类如果尝试初始化也将进入失败状态。前进的唯一方法是卸载所有失败的类。对于动态加载的代码3来说这可能是可行的,但总的来说不是。
1-如果静态块无条件抛出未经检查的异常,则是编译错误。
2-您可以通过注册默认的未捕获异常处理程序来拦截它,但这将使您无法恢复,因为您的“主”线程无法启动。
3-如果要恢复失败的类,则需要摆脱加载它们的类加载器。
这个设计决定背后的原因是什么?
这是为了保护程序员避免编写引发无法处理的异常的代码!
正如我们所看到的,静态初始化程序中的异常将典型的应用程序变为砖头。语言设计师可以做的最好的事情是将检查的情况视为编译错误。(不幸的是,对于未经检查的异常也不能这样做。)
好的,如果您的代码“需要”在静态初始化程序中引发异常,该怎么办。基本上,有两种选择:
如果可以从块内的异常中恢复(完全!),请执行此操作。
否则,请重组代码,以使初始化不会在静态初始化块(或静态变量的初始化器)中发生。
看一下Java语言规范:它指出,如果静态初始化程序失败 并能够通过检查的异常突然完成,则这是编译时错误。
public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }
输出:Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: whathappenswhenastaticblockthrowsanexception at Main.<clinit>(Main.java:6) Caused by: java.lang.ClassNotFoundException: whathappen...
由于您编写的任何代码都不能调用静态初始化块,因此抛出checked没有用exceptions
。如果有可能,当抛出检查异常时,jvm会做什么?Runtimeexceptions
向上传播。
例如:Spring的DispatcherServlet(org.springframework.web.servlet.DispatcherServlet)处理捕获了一个已检查的异常并引发另一个未检查的异常的场景。
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
我也可以通过编译来检查异常。
static {
try {
throw new IOException();
} catch (Exception e) {
// Do Something
}
}