字段初始化中未处理的异常

28

Java有没有语法来管理在声明和初始化类成员变量时可能抛出的异常?

public class MyClass
{
  // Doesn't compile because constructor can throw IOException
  private static MyFileWriter x = new MyFileWriter("foo.txt"); 
  ...
}

或者这样的初始化是否总是需要移动到一个方法中,我们可以在该方法中声明throws IOException或在try-catch块中包装初始化?


akf的回答是正确的,但如果你不喜欢声明新方法,你也可以使用静态块来初始化你的静态变量;你可以在静态块中使用try/catch。 - C. K. Young
不要让其他人误解我上一条评论是支持静态块的方法:我更喜欢创建新的方法。我只是为那些不喜欢这种方法的人提供了一个替代方案。 :-) - C. K. Young
抱歉,我忘记将该字段标记为静态的了。这个建议仍然适用吗? - kpozin
或者您可以使用非静态(即实例)初始化块来初始化非静态字段。 Try/catch同样适用。(有些人认为这是一个“隐藏功能”,https://dev59.com/HHVD5IYBdhLWcg3wVKAb#47493) - Jonik
7个回答

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
  ...
}

3
如果你想在处理完成后继续传播,你还可以选择抛出一个 RuntimeException。 - akf
如果您有多个类具有MyFileWriter成员,那么这不是一个问题吗?您将不得不在每个类中重新编写静态块。 - Tom
既然它是一个类成员,那这不是理所当然的吗? - MattC
2
如果MyFileWriter必须初始化才能使类正常运行,那么应该抛出RuntimeException来防止类被加载。这将有效地使任何试图使用此类的代码注定失败,如果它对您的应用程序运行至关重要,则可能是期望的行为。如果不这样做记录日志,每次调用MyClass方法都有可能失败。 - Robin

3

最佳实践是将这些初始化移动到能够正确处理异常的方法中。


同意。我在我的答案中实现了一个工厂方法。 - Tom

2

正如你所发现的那样,这种构造方式是不合法的。如果成员的构造函数抛出已检查异常,则只有在允许异常处理的上下文中(例如构造函数、实例初始化程序或(对于静态成员)静态初始化程序)才能构造它们。

因此,以下是一种合法的方法:

public class MyClass {
    MyFileWriter x;
    {
        try {
            x = new MyFileWriter("foo.txt");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    ...
}

虽然合法,但相当丑陋。我更喜欢在构造函数中初始化并声明异常,或者让用户调用一个方法来明确初始化它。但是如果用户必须初始化它,则必须在任何依赖的方法中考虑该对象无效的可能性。

如果您正在将MyClass作为MyFileWriter的包装器编写,我建议在构造函数中进行初始化。否则,我首先会质疑在对象的整个生命周期内是否有必要打开写入器。可能可以通过重构来消除这种情况。

编辑:当我撰写此文时,字段没有添加"static"。这改变了很多东西:现在我想知道你为什么要在类加载器的整个生命周期中打开写入器。它怎么可能被关闭呢?

这是某种自制的日志记录系统吗?如果是,我鼓励您查看java.util.logging或其他许多出色的第三方日志记录框架。


2
从静态初始化器抛出的异常可能表明存在设计问题。实际上,您不应尝试将文件加载到静态变量中。此外,静态变量通常不应该是可变的。
例如,回顾使用JUnit 3.8.1时,您几乎可以从applet / WebStart中使用它,但由于一个静态初始化器进行文件访问而失败。涉及的类的其余部分都适合上下文,只有这个静态部分不适合上下文,并使整个框架崩溃。
有一些合法的情况会抛出异常。如果环境没有特定功能(例如因为它是旧的JDK),则可能要替换实现,这是很正常的。如果类真的已经损坏,请抛出未经检查的异常,而不是允许存在损坏的类。
根据您的喜好和手头的问题,有两种常见的解决方法:显式的静态初始化器和静态方法。(我认为大多数人更喜欢前者;我相信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);
    }
}

注意:静态属性应该是final(通常是不可变的)。通过使用final,您友好的编译器会检查正确的单一赋值。确定性分配意味着它可以捕获破碎的异常处理-包装并抛出,而不是打印/记录。奇怪的是,在静态初始化程序中你不能使用类名来限定初始化(我相信这有一个很好的原因)。
实例初始化程序类似,尽管您可以使构造函数抛出或将初始化程序放置在构造函数内部。

-1

在字段初始化中处理异常的另一种方法是使用try-catch语句。假设您的情况是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对象。


-1
如果一个类只有一个构造函数,我通常会将这些初始化程序移动到该构造函数中。
如果一个类有多个构造函数,我会使用初始化块:
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块的好处在于它被“注入”到每个构造函数的开头。因此,您不需要重复初始化程序。

-1
我建议使用工厂方法:
  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.

    } 
  }

2
这并不能解决问题,你在调用getFileWriter()时没有异常处理,要加入异常处理,你需要放置一个静态块。 - Robin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接