假设我有一个需要在其构造函数中进行初始化工作的类。没有理由相信它会失败,但如果它真的发生了,那么这个实例(也可能是应用程序;让我们考虑这些是两个单独的情况)将会处于无法使用的状态,并且没有真正希望在该范围内恢复(实例或全局)。处理构造函数中引发的致命异常
在Java中处理这种情况的推荐方法是什么?将异常封装在运行时异常中似乎是一种自然的方法,但是有没有关于哪种异常类型用于这种目的的建议或共识?
假设我有一个需要在其构造函数中进行初始化工作的类。没有理由相信它会失败,但如果它真的发生了,那么这个实例(也可能是应用程序;让我们考虑这些是两个单独的情况)将会处于无法使用的状态,并且没有真正希望在该范围内恢复(实例或全局)。处理构造函数中引发的致命异常
在Java中处理这种情况的推荐方法是什么?将异常封装在运行时异常中似乎是一种自然的方法,但是有没有关于哪种异常类型用于这种目的的建议或共识?
如果您的构造函数中的某些内容抛出异常,您可以声明它自己抛出异常,也可以用一个更一般的异常(如自己的InstantiateXxxxException)。
通常,您有多种类型的Throwable
具有不同的含义。
如下我想它们进行分类:
至少在这种特殊情况下,我最终决定将异常封装在“RuntimeException”中。如果需要,它给予调用者更优雅的处理情况的机会,并不要求我处理真正例外的异常(如果库初始化失败,部署可能有问题),并表达意图表达这可能不容易恢复,特别是来电者。我相信这对我来说足够好。 – 2011-03-14 10:04:17
这取决于出了什么问题。我的意思是,如果它因为空值而失败,那么NullPointerexception,如果你遇到了不是预期的状态,那么可能是IllegalStateException。
但我会建议,而不是暴露构造函数只是有一个静态工厂方法,您可以命名,并可以优雅地处理这种情况。 例如
public class A{
private A() {}
private init() { .. }
public static A getNewInstance() { // may be parameters if you want
A a = new A();
a.init();
}
}
我只是给了init
一个例子,但你可以有其他选择为好,关键是你可以有静态方法来处理这样的东西比构造,如果构造是处理复杂的逻辑。
如果整个应用程序处于不稳定状态,我建议直接退出应用程序以避免更多问题。在输出system.err
之前。
如果只有实例处于不稳定状态,则可以抛出运行时异常,但我认为这是一种危险的方式,因为它们没有义务捕获异常,这可能会导致稍后出现一些问题。
我建议简单地重新抛出异常,或者用其他相关信息将其包装在另一个中。异常的确切类型很大程度上取决于案例(空指针,非法状态,io错误等)
请不要在构造函数中做'System.exit()'。永远。首先,构造函数不应限定应用程序是否处于不可用状态,其次,即使它引发了“错误”也是适当的方式来进行干净的通信。 – 2011-03-11 16:00:49
@Mark首先,您不会提供任何形式的论据来支持您的评论。然后,OP明确地询问如果初始化失败使应用程序处于不稳定状态该怎么办,我不是在这里判断它的构造函数是否合格或不作出此决定。通过抛出异常,您可能会面临处理不当并且应用程序不能退出的风险。最后,我从来没有说过要使用System.exit(),只是退出应用程序而未指定执行方式。顺便说一句,感谢downvote;) – krtek 2011-03-11 16:09:35
也许你可以建议还有什么“直接退出应用程序”可能意味着那么。如果某些schmuck想要捕获一个'Error'(注意我没有说'Exception'),那么这是他作为代码的客户端的特权,并且他承担了风险。这与捕捉“OutOfMemoryError”没有什么不同。我试图说从构造函数退出必然意味着构造函数对应用程序有太多的控制权和洞察力,这违背了大多数设计原则(耦合等)。这是我的观点,我在原评论中添加了一个较短的版本。 – 2011-03-11 16:31:57
如果异常在对象的构造函数抛出的,它并不比一个方法调用发现异常太大的不同,但你能保证你的对象没有获得建造
NewObject obj = null;
try {
obj = new NewObject() {
} catch (Throwable t) {
// obj is null.
}
是否可以从这种异常中恢复很大程度上取决于抛出的异常。预计会检查异常,但偶尔发生错误;通常他们是最容易恢复的。未经检查的异常处理诸如除以零之类的事情(其中必须抓住每一个可能的抛出将是麻烦的)。错误是由于标准假设关于正常JVM操作没有保持(比如分配更多内存的能力)而导致程序可能失败的情况。
有可能从某些JVM错误中恢复;但是,您希望从错误中恢复过来的错误以及恢复的方式需要仔细计划。典型地,一个见下从JVM错误具有低存储器条件的处理(伪java代码如下)时恢复
Runtime runtime = Runtime.getRuntime();
while (true) {
Cache myCache = new SomeCache();
try {
Message message = messages.getMessage();
if (!(cache.hasKey(message))) {
Result result = messageProcessor.getResult(message);
cache.put(message, result);
}
message.getReplyChannel().send(cache.get(message));
} catch (OutOfMemoryException e) {
myCache.clear();
runtime.gc();
messages.putBack(message);
}
}
这样的解决方案的值很大程度上依赖于系统设计的其余部分。假设“缓存”的最大大小有限制,如果程序的另一部分启动内存占用,这可能仍然有用。关键是要确保你的内存释放操作取决于已经分配的对象,因为它试图释放内存,所以不会对系统提出更多的内存需求。
你甚至可以声明你的构造函数抛出一个检查的异常 – 2011-03-11 15:36:28
因此,检查的异常是不是可以干净地暴露给你的构造函数调用者?像I/O异常一样? Socket的构造函数抛出检查异常,例如 – 2011-03-11 15:37:51
虽然有时候,我不能或不想按照原样抛出异常。例如,当我实例化一个使用OpenSAML库的类时,我可能不想用'throws org.opensaml.xml.ConfigurationException'修饰构造函数(或一堆方法),但是如果抛出异常,试图继续下去,只会让我更深入地了解其他例外情况。 – 2011-03-11 15:49:08