2

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

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

TheConstructor() throws SomeException {
    if(alreadyInited) {
        return;
    }
    alreadyInited = true;
    foo();
}

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

4

5 回答 5

3

是否允许静态类构造函数中的异常以逃避正确的设计模式?

我会说不。

首先,在该上下文中包装已检查的异常意味着如果出现问题,您将获得类初始化失败。

  • 传播出类初始化程序的异常不能被直接捕获和处理。

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

更大的图景是,这可能代表了对静力学的不当使用。有更好的方法来处理这个问题;例如

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

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

于 2012-05-16T22:15:27.350 回答
1

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

于 2012-05-16T21:53:43.970 回答
1

你的第一个例子对我来说很好。如果您的课程绝对需要做foo()才能使用,那么如果它不能做foo(),那么它就无法使用。如果它不可用,那么它一定不能使用。从静态初始化程序中抛出异常是确保不使用该类的直接且有效的方法。

不幸的是,Java 不允许从静态初始化程序中抛出已检查的异常。我不明白为什么会这样。

于 2012-05-16T22:29:12.223 回答
0

您可以像这样使第二个不那么笨拙:

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()。使用选项 1,调用将触发类初始化;使用选项2,它不会。

于 2012-05-16T22:09:43.160 回答
0

在这种情况下,我为“配置”选择了单例模式。

但是构造函数内部的逻辑很奇怪,静态块更干净恕我直言,但我认为不容易测试。当心。

于 2012-05-16T21:55:28.280 回答