构造函数是否允许抛出异常?
是的,构造函数可以抛出异常。通常这意味着新对象会立即变为可回收的(当然可能需要一些时间才能被回收)。如果在构造函数中使自己变得可见(例如通过分配静态字段或将自己添加到集合中),那么"半构造"对象可能会保留下来。
在构造函数中抛出异常时需要小心一件事:因为调用者(通常)没有办法使用新对象,因此构造函数应该小心避免获取未管理的资源(例如文件句柄等),然后在不释放它们的情况下抛出异常。例如,如果构造函数尝试打开一个FileInputStream
和一个FileOutputStream
,并且第一个成功但第二个失败,则应尝试关闭第一个流。当然,如果是子类构造函数引发异常,这就变得更加困难了……这一切都变得有点棘手。这种情况并不经常发生,但值得考虑。
是的,它们可以抛出异常。如果出现异常,它们只会被部分初始化,并且如果不是final,则容易受到攻击。
以下内容来自Secure Coding Guidelines 2.0。
非final类的部分初始化实例可以通过finalizer攻击进行访问。攻击者在子类中重写受保护的finalize方法,并尝试创建该子类的新实例。虽然这次尝试失败了(在上面的示例中,ClassLoader的构造函数中的SecurityManager检查会引发安全异常),但攻击者仍然会忽略任何异常并等待虚拟机对部分初始化对象执行终结操作。当这种情况发生时,恶意finalize方法实现将被调用,使攻击者能够访问这个正在被终结的对象的引用。虽然该对象仅被部分初始化,但攻击者仍然可以在其上调用方法(从而规避SecurityManager检查)。
finalize()
方法将永远不会被调用。此外,在给实例字段分配任何值之前,应始终检查所有值,因为这样,“部分初始化”意味着“无法使用”,因此不存在安全风险。 - Holger当构造函数没有收到有效的输入,或者无法以有效的方式构造对象时,它除了抛出异常并警告其调用者外别无选择。
是的,它可以抛出异常,你也可以在构造函数的签名中声明它,如下例所示:
public class ConstructorTest
{
public ConstructorTest() throws InterruptedException
{
System.out.println("Preparing object....");
Thread.sleep(1000);
System.out.println("Object ready");
}
public static void main(String ... args)
{
try
{
ConstructorTest test = new ConstructorTest();
}
catch (InterruptedException e)
{
System.out.println("Got interrupted...");
}
}
}
是的,构造函数是允许抛出异常的。
但是,在选择应该抛出哪些异常时,要非常明智 - 是检查异常还是未经检查的异常。未经检查的异常基本上是RuntimeException的子类。
在几乎所有情况下(我无法找到一个例外),您将需要抛出已检查的异常。原因是未经检查的异常(如NullPointerException)通常是由于编程错误(例如未充分验证输入)而导致的。
已检查的异常提供的优点是,程序员被强制在实例化代码中捕获异常,并因此意识到可以创建对象实例失败。当然,只有代码审查才能发现吞咽异常的不良编程实践。
是的。
构造函数只是特殊的方法,和其他方法一样可以抛出异常。
someStaticField = this;
或someCollection.add(this)
。 - Jon Skeet