为什么Java不允许从静态初始化块中抛出已检查的异常?

mis*_*tor 128 java exception static-initializer

为什么Java不允许从静态初始化块中抛出已检查的异常?这个设计决定背后的原因是什么?

Kos*_*801 113

因为无法在源中处理这些已检查的异常.您无法控制初始化过程,并且无法从源调用静态{}块,因此您可以使用try-catch包围它们.

因为您无法处理由已检查异常指示的任何错误,所以决定禁止抛出已检查的异常静态块.

静态块不能抛出已检查的异常,但仍允许抛出未经检查的/运行时异常.但根据上述原因,你也无法处理这些问题.

总而言之,这种限制阻止(或者至少使开发人员更难以)构建可能导致应用程序无法恢复的错误的东西.

  • 实际上,这个答案是不准确的.您可以在静态块中抛出异常.您不能做的是允许*checked*异常*从*静态块传播出来. (66认同)
  • 如果您自己使用Class.forName(...,true,...)进行动态类加载,则可以处理此异常; 当然,这不是你经常遇到的事情. (15认同)
  • @KirillBazarov一个带有静态初始值设定项的类总是导致异常将无法编译(因为它为什么要这样?).在if子句中包含throw语句,你很高兴. (4认同)
  • static {throw new NullPointerExcpetion()} - 这也不会编译! (2认同)
  • @Ravisha NullPointerException 是允许的,因为它是 RuntimeException 而不是已检查的异常。 (2认同)
  • @Ravisha因为在这种情况下初始化程序无法在任何情况下正常完成.使用try-catch,println可能不会抛出任何异常,因此初始化程序有机会完成,无例外.这是异常的无条件结果,使其成为编译错误.请参阅JLS:https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.7但是编译器可能仍然会因为在您的情况下添加一个简单的条件而被欺骗:`static {if(1 <10){throw new NullPointerException(); } (2认同)
  • @Thamiar - 因为它是无条件抛出的,并且因为您没有在静态块中捕获它。见上面 Kosi2801 的评论。 (2认同)

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)

  • 是的,你可以做到这一点,但这是一个非常糟糕的主意.未经检查的异常会使类和依赖于它的任何其他类*失败*状态只能通过卸载类来解决.这通常是不可能的,`System.exit(...)`(或等效的)是你唯一的选择, (4认同)

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'静态初始化器的接口)必须抛出异常或处理它 - 在接口处进行异常处理?最好保持原样.

  • `main`可以抛出已检查的异常.显然那些无法处理. (7认同)
  • @Mechanicalsnail:有趣的一点。在我的 Java 思维模型中,我假设有一个“神奇的”(默认)Thread.UncaughtExceptionHandler 附加到运行“main()”的线程,该线程将异常和堆栈跟踪打印到“System.err”,然后调用“System” .exit()`。最后,这个问题的答案很可能是:“因为Java设计者这么说”。 (3认同)

Ste*_*n C 7

为什么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上面抓住ExceptionInInitializerError2.

在某些情况下,你可以抓住它.例如,如果通过调用触发了类初始化Class.forName(...),则可以将调用括在a中try并捕获ExceptionInInitializerError或后续NoClassDefFoundError.

但是,如果您试图从中恢复,ExceptionInInitializerError则可能会遇到障碍.问题是在抛出错误之前,JVM将导致问题的类标记为"失败".你根本无法使用它.此外,依赖于失败类的任何其他类如果尝试初始化也将进入失败状态.前进的唯一方法是卸载所有失败的类.这对于动态加载的代码3 可能是可行的,但通常不是.

1 - 如果静态块无条件地抛出未经检查的异常,则会出现编译错误.

2 - 您可以通过注册默认的未捕获异常处理程序来拦截它,但这不允许您恢复,因为您的"主"线程无法启动.

3 - 如果要恢复失败的类,则需要删除加载它们的类加载器.


这个设计决定背后的原因是什么?

它是为了保护程序员不要编写抛出无法处理的异常的代码!

正如我们所看到的,静态初始化程序中的异常将典型应用程序变为砖块.最好的事情是语言设计者可以做的是将已检查的案例作为编译错误来处理.(不幸的是,对于未经检查的例外,这也是不切实际的.)


好的,那么如果您的代码"需要"在静态初始化程序中抛出异常,您应该怎么做.基本上,有两种选择:

  1. 如果(完全!)可以从块中的异常中恢复,那么就这样做.

  2. 否则,重构代码,以便初始化不会发生在静态初始化块(或静态变量的初始化器)中.

  • 1)我没有。2)它们听起来很糟糕。请参阅我对他们留下的评论。但我只是重复我在上面的回答中所说的话。如果您阅读并理解我的答案,您就会知道那些“解决方案”并不是解决方案。 (2认同)