【问题标题】:Java no-argument constructor: Throw impossible exception, or have empty catch block?Java无参数构造函数:抛出不可能的异常,或者有空的catch块?
【发布时间】:2012-05-05 04:25:26
【问题描述】:

无参数构造函数是抛出一个不可能的异常还是有一个空的catch块更好?假设我有一堂这样的课。

public class Foo {
    private int num;
    private String numString;

    public Foo() throws NumberFormatException {
        setNum("1");
    }

    public void setNum(String s) throws NumberFormatException {
        // In reality, complex code goes here
        num = Integer.parseInt(s);
        numString = s;
    }
}

编译器强制构造函数要么抛出 NumberFormatException(这永远不会发生),要么有一个 try/catch 块。但是,有一个空的 catch 块是否正确,这通常是不受欢迎的?

public Foo() {
    try {
        setNum("1");
    } 
    catch (NumberFormatException e) { }
}

请注意,Foo 将是一个库类,其他人会使用它,所以让一个无参数构造函数抛出一个不可能的异常会令人困惑。另请注意,真正的异常是自定义异常,而不是 NumberFormatException,这可能会使库用户更加困惑,他们可能会觉得他们必须在不需要时阅读自定义异常。

【问题讨论】:

    标签: java exception constructor


    【解决方案1】:

    NumberFormatException 是一个RuntimeException,所以你不需要在你的throws 子句中列出它——假装它不存在。这适用于方法和构造函数。

    RuntimeException 的任何子类(包括RuntimeException 本身)都是“未经检查的异常”,这意味着编译器不会强制您在 try/catch 子句中检查它。相比之下,任何不是RuntimeException 子类的Exception 都会很容易地被称为检查异常。

    如果它已检查异常(即,不是RuntimeException 的子类),但您确定永远无法触发它,那么捕获它是安全的。与其完全吞下它,不如将它包裹在一个未经检查的异常周围。

    try {
        thisCodeCanNeverThrowAnIOException();
    }
    catch (IOException e) {
        throw new AssertionError(e); // juuust in case!
    }
    

    现在您的代码可以编译,没有 throws 子句来表示您从未期望抛出的异常,但如果由于某些错误或未来的更改,该异常最终会得到,则不会隐藏严重错误扔了。

    (阅读 cmets 的人请注意:我最初在代码示例中使用了 throw new RuntimeException(e),但 Stephen C 和 Peter Lawrey 指出,在 Java 1.4.2 和更新版本中,AssertionError 更好)。

    【讨论】:

    • +1 - 我更喜欢抛出“AssertionError”,它具有“这不应该发生”的更具体含义。
    • 重新抛出一个包含在未经检查的异常中的检查异常,例如RuntimeException,这是一个由来已久的传统。只要客户端代码没有捕捉到ThrowableException 被吞噬,最终将记录或报告编程错误。一些人认为,已检查与未检查异常的概念创造了一种忽略实际问题的文化。制定合理的异常政策对于构建强大的应用程序至关重要。
    • @yshavit - 这不再正确。从 Java 7 开始,AssertError 确实有一个采用 Throwable 原因的构造函数 - docs.oracle.com/javase/7/docs/api/java/lang/…
    • @yshavit 从 Java 1.4.2 开始,AssertionError 将 Throwable 作为参数作为原因。 docs.oracle.com/javase/1.4.2/docs/api/java/lang/…
    • @PeterLawrey 也感谢你,除了 SO 只允许我 @-ify 一个用户...
    【解决方案2】:

    既然 s 必须是一个数字,为​​什么不通过 setNum() 传递一个整数而不是当前字符串?整数始终可以解析为字符串。

    【讨论】:

    • 我的真实方法将一个字符串数组作为参数,并且数组中的每个字符串都需要进行验证,否则会引发异常。我使用这个例子是因为它更容易。
    【解决方案3】:

    我会说这取决于您的业务关怀或个人编码风格,如果您选择抛出异常,如果您传入的参数不正确或没有意义,那么使用拥有和包装的异常可能会更好现在可以抛出异常。

    无论 NumberFormatException 必须抛出什么,这都是你在哪里捕获它的公正决定。它可能在您的库类、调用者类或您不知道的某个地方。没有它,程序将会关闭。

    在您的情况下,您是否将无编号字符串视为合法值,如果是,则您有责任将异常保留在您的包中。

    【讨论】:

      【解决方案4】:

      我会说:永远不要一个空的 catch 块(无论如何)。正如@yshavit 指出的那样,吞下可能的错误并不是一个好习惯。

      正如@stephen-c 所说,使用断言是向前迈出的一步,但此解决方案有两个缺点:

      1. 可能没有在“操作/生产”服务器上启用断言,所以您真正拥有的非常类似于一个空的 catch 块
      2. 如果在服务器上启用了它们,则在执行这段代码期间出现错误将表明您的应用程序中存在错误,只要它出现了意想不到的事情(“这不应该发生”,但它发生了!所以...)

      所以我的建议是@yshavit 提案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多