Java是否有任何语法来管理在声明和初始化类的成员变量时可能抛出的异常?
public class MyClass
{
// Doesn't compile because constructor can throw IOException
private static MyFileWriter x = new MyFileWriter("foo.txt");
...
}
或者这些初始化总是必须转移到我们可以声明throws IOException
或将初始化包装在try-catch块中的方法中吗?
答案 0 :(得分:20)
使用静态初始化块
public class MyClass
{
private static MyFileWriter x;
static {
try {
x = new MyFileWriter("foo.txt");
} catch (Exception e) {
logging_and _stuff_you_might_want_to_terminate_the_app_here_blah();
} // end try-catch
} // end static init block
...
}
答案 1 :(得分:3)
最佳做法是将这些类型的初始化移动到可以处理异常属性的方法。
答案 2 :(得分:3)
静态初始化程序引发的异常可能表示存在设计问题。真的,你不应该试图将文件加载到静态。另外,静态不应该是可变的。
例如,使用JUnit 3.8.1,您几乎可以从applet / WebStart中使用它,但由于一个静态初始化程序执行文件访问,它失败了。所涉及的其他类都适合上下文,这只是静态的一点,不符合上下文并且吹掉了整个框架。
有一些合法的情况会引发异常。如果环境没有特定功能,例如,因为它是一个旧的JDK,那么你可能想要替换实现,并没有什么不寻常的。如果该类确实被borked,则抛出一个未经检查的异常,而不是允许存在破坏的类。
根据您的偏好和手头的问题,有两种常见的解决方法:显式静态初始化和静态方法。 (我,我认为大多数人,更喜欢前者;我相信Josh Bloch更喜欢后者。)
private static final Thing thing;
static {
try {
thing = new Thing();
} catch (CheckedThingException exc) {
throw new Error(exc);
}
}
或者
private static final Thing thing = newThing();
private static Thing newThing() {
try {
return new Thing();
} catch (CheckedThingException exc) {
throw new Error(exc);
}
}
注意:静态应该是最终的(通常是不可变的)。作为最终,友好的编译器会检查正确的单一作业。确定赋值意味着它可以捕获破坏的异常处理 - 包装和抛出,不打印/记录。奇怪的是,您不能使用类名称来使用静态初始化程序中的类名限定初始化(我确信这有充分的理由)。
实例初始化器类似,但您可以使构造函数抛出,或者您可以将初始化器放在构造函数中。
答案 3 :(得分:2)
正如您所发现的那样,这种结构是非法的。除非在允许异常处理的上下文中(例如构造函数,实例初始化程序或(对于静态成员)静态初始化程序),否则无法构造构造函数抛出已检查异常的成员。
所以这是一种合法的方式:
public class MyClass {
MyFileWriter x;
{
try {
x = new MyFileWriter("foo.txt");
} catch (IOException ex) {
ex.printStackTrace();
}
}
...
}
合法,但相当丑陋。我更喜欢在构造函数中初始化它并在那里声明异常,或者让用户调用一个方法来显式初始化它。但是,如果用户必须对其进行初始化,则必须在任何相关方法中考虑该对象无效的可能性。
如果您正在编写MyClass
作为MyFileWriter
的包装器,我会说要在构造函数中进行初始化。否则,我首先会质疑是否有必要在对象的整个生命周期内打开一个作者。有可能将其重构掉。
编辑:当我写这篇文章时,“static
”尚未添加到该字段中。这改变了很多事情:我现在想知道为什么你想让一个作家为类加载器的整个生命周期开放。怎么会被关闭?
这是一种本土的日志记录系统吗?如果是这样,我建议您查看java.util.logging
或周围许多优秀的第三方日志框架中的任何一个。
答案 4 :(得分:0)
如果类只有一个构造函数,我通常会将这些初始化程序移动到该构造函数中。
如果类有多个构造函数,我使用初始化块:
public class MyClass {
private static MyFileWriter x;
// initialization block start here
{
try {
x = new MyFileWriter("..");
} catch(Exception e) {
// exception handling goes here
}
}
public MyClass() {
// ctor #1
}
public MyClass(int n) {
// ctor #2
}
}
关于init的好处。块是它被“注入”到每个构造函数的开头。因此,您无需复制初始值设定项。
答案 5 :(得分:0)
我建议使用工厂方法:
public class MyClass{
private static MyFileWriter fileWriter = MyFileWriter.getFileWriter("foo.txt");
}
public class MyFileWriter {
/*
* Factory method. Opens files, etc etc
* @throws IOException.
*/
public static MyFileWriter getFileWriter(String path) throws IOException{
MyFileWriter writer = new FileWriter();
//stuff that can throw IOException here.
return writer;
}
/*protected constructor*/
protected MyFileWriter(){
//build object here.
}
}
答案 6 :(得分:0)
还有另一种方法可以在字段初始化中处理异常。让我们考虑你的情况,MyFileWriter构造函数抛出异常。请考虑此示例代码以供参考。
import java.io.IOException;
public class MyFileWriter {
public MyFileWriter(String file) throws IOException{
throw new IOException("welcome to [java-latte.blogpspot.in][1]");
}
}
如果我们尝试初始化这样的文件.....
public class ExceptionFields{
MyFileWriter f = new MyFileWriter("Hello");
}
编译器不允许我们初始化它。您可以这样做
,而不是在静态块中进行初始化声明抛出IOException的默认构造函数
import java.io.IOException;
public class ExceptionFields{
MyFileWriter f = new MyFileWriter("Hello");
public ExceptionFields() throws IOException{
}
}
这将初始化您的MyFileWriter对象。