Java 允许静态类构造函数中的异常转义是正确的设计模式吗?

Java 允许静态类构造函数中的异常转义是正确的设计模式吗?,java,exception,static-initialization,Java,Exception,Static Initialization,我发现自己经常在以下两种设计模式中进行选择: static { try { foo(); } catch(Exception exc) { throw new RuntimeException(exc.getMessage()); } } 及 问题是我真的希望每个类初始化一次东西——我认为是静态的——比如设置一个记录器,从一个文件加载一些映射,等等——如果这个操作失败,我真的希望程序停止。这两种设计模式看起来都很笨拙(第一种更明显

我发现自己经常在以下两种设计模式中进行选择:

static {
    try {
        foo();
    }
    catch(Exception exc) {
        throw new RuntimeException(exc.getMessage());
    }
}


问题是我真的希望每个类初始化一次东西——我认为是静态的——比如设置一个记录器,从一个文件加载一些映射,等等——如果这个操作失败,我真的希望程序停止。这两种设计模式看起来都很笨拙(第一种更明显),所以我想知道是否有更好的方法来实现这一点。

工厂模式可以在这里帮助您。您仍然可以抛出运行时异常,尽管我会抛出一个名为“CouldNotCreate”的异常以及一些有关出错原因的酷信息。

在本例中,我选择了“配置”的单例模式


但是构造函数内部的逻辑非常奇怪,静态块更干净,但是我认为它不容易测试。小心点。

你可以让第二个变得不那么麻烦,就像这样:

static boolean isStaticInit = false;
static void staticInit() throws InitException {
    if (isStaticInit) return;

    // [init loggers, map, etc...throw InitException as appropriate]

    isStaticInit = true;
}

MyClass() throws InitException {
    staticInit();

    // ...
}
请注意,您的第一个选项和第二个选项之间存在细微差别。在第一个选项中,在第一次加载类时调用
static{}
块,而在第二个选项中,在构造第一个类实例之前不会调用init代码。如果您有调用MyClass.myStaticMethod()的代码,比如说,
MyClass.myStaticMethod()
,这可能会有所不同。使用选项1,调用将触发类初始化;有了选项2,它就不会了

允许静态类构造函数中的异常转义是正确的设计模式吗

我会说不

首先,在该上下文中包装一个选中的异常意味着,如果出现问题,将导致类初始化失败

  • 无法捕获并直接处理在类初始化器之外传播的异常

  • 类初始化期间未捕获的异常不可恢复。即使您可以捕获并处理异常,该类和(我认为)依赖于它的所有其他类/它们也将处于无法初始化的状态,因此无法使用。对于(至少)类加载器和整个JVM来说,这实际上是“游戏结束了”

更大的情况是,这可能代表了对静力学的不当使用。有更好的方法来处理这个问题;e、 g

  • 使用显式调用(在可以捕获和处理异常的地方)进行初始化,而不是依赖于类初始化

  • 摆脱静态(可能还有包装它的单例类)并使用依赖注入


    • 我觉得你的第一个例子很好。如果您的类绝对需要执行
      foo()
      才能使用,那么如果它不能执行
      foo()
      ,则它不可用。如果它不可用,则不得使用。从静态初始值设定项引发异常是确保不使用该类的一种简单而有效的方法


      不幸的是,Java不允许从静态初始值设定项抛出已检查的异常。我不明白为什么会出现这种情况。

      为什么不使用单例?问题是我真的很想在每个类中初始化一次东西。你在找单例吗?另外,我不太明白为什么您的第一个和第二个代码示例在功能上是等效的……如果您的初始化代码失败,您的程序还能运行吗?也许System.exit()是一个幻觉。都是真的。只想再添加两个:1)每个类初始化一次并不意味着静态初始化。您可以使用适当的架构和设计来实现这一点。2) 在类初始化过程中使用异常这样丑陋的东西在模拟测试中会是一件非常痛苦的事情。别这样it@piotrek-测试是一个很好的观点,但你的观点1)基本上重复了我在第三个要点中所说的(或至少我的意思是)接受这一点,因为我确实认为正确的设计模式是运行时/客户机代码控制是否包含此模块。我认为在我的实际应用程序中,代码足够简单,因此这种方法比“适当的”设计模式具有足够的可维护性和更高的可维护性。factory模式有何帮助?请将静态初始化部分中的所有内容移到factory类中。
      static boolean isStaticInit = false;
      static void staticInit() throws InitException {
          if (isStaticInit) return;
      
          // [init loggers, map, etc...throw InitException as appropriate]
      
          isStaticInit = true;
      }
      
      MyClass() throws InitException {
          staticInit();
      
          // ...
      }