在Java中生成致命错误

时间:2013-10-29 07:00:06

标签: java exception-handling

假设我们正在编写一个Java库,它提供了一些I / O ulitity函数,例如,一个方便的方法来读取文本文件作为字符串:

public class StringReader {

private static final Logger log = LoggerFactory.getLog(StringReader.class);

/**
 * Returns the contents of file <b>fileName</b> as String.
 * @param fileName file name to read
 * @return null on IO error
 */
public static String readString(String fileName) {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(fileName);
        byte[] data = new byte[fis.available()];
        fis.read(data);
        return new String(data, "ISO-8859-1"); // may throw UnsupportedEncodingException!
    } catch (IOException e) {
        log.error("unable to read file", e);
    } catch (UnsupportedEncodingException e) {
        log.fatal("JRE does not support ISO-8859-1!", e);
        // ???
    } finally {
        closeQuiet(fis);
    }

    return null;
}
}

此代码使用ISO-8859-1编码将文本文件读入String,并将String返回给用户。

String(byte[], String)构造函数在不支持指定编码时抛出UnsupportedEncodingException。但是,正如我们所知,ISO-8859-1必须得到JRE的支持,如here (see the Standard charsets section)所述。

因此,我们期待块

catch (UnsupportedEncodingException e) {
    log.fatal("encoding is unsupported", e);
    // ???
}
如果JRE分发符合标准,则永远不会达到

但如果没有呢?如何以最正确的方式处理此异常? 问题是,如何正确警告这种错误?

建议如下:

  1. 抛出某种RuntimeException
  2. 不要在生产代码中禁用记录器,在日志中写入异常详细信息并忽略它。
  3. assert false放在此处,如果用户使用-ea启动了VM,则会产生AssertionError。
  4. 手动投掷AssertionError
  5. 在方法声明中添加UnsupportedEncodingException并允许用户选择。我认为不太方便。
  6. 致电System.exit(1)
  7. 感谢。

2 个答案:

答案 0 :(得分:7)

  

但如果没有呢?

然后你处境非常糟糕,你应该尽可能快地摆脱它。当JRE违反自己的承诺时, 会依赖什么?

在这种情况下,我会很高兴使用AssertionError

重要的是要注意并非所有未经检查的异常都被平等对待 - 代码在堆栈的顶层捕获Exception,记录错误然后继续...并不常见...如果你只是抛出RuntimeException被此类计划捕获。仅当catch块指定AssertionError(或具体为ThrowableError时才会捕获AssertionError,但这种情况很少见。鉴于这应该是多么不可能,我认为真的很难中止是合理的。

另请注意,在Java 7中,您可以使用StandardCharsets.ISO_8859_1而不是字符串名称,这更清晰并且可以解决问题。

顺便说一句,关于你的代码还有其他一些改变:

  • 我会尽量避免使用available()。这告诉你现在有多少字节可用 - 它不会告诉你文件有多长,必然。
  • 肯定不假设read()将一次读取整个文件。在循环中调用read(),理想情况下直到它说没有更多数据。
  • 我个人会接受Charset作为参数,而不是硬编码ISO-8859-1。 - 我会让IOException从方法中冒出来,而不仅仅是返回null。毕竟,除非你真的要检查每个无效调用的返回值,否则你最终会得到NullPointerException,这比诊断更难原IOException

或者,只需使用GuavaFiles.toString(File, Charset)开头:)(如果你还没有使用过番石榴,现在是开始的好时机......)

答案 1 :(得分:5)

这在代码中很常见。

为此进行了未经检查的例外情况。它们不应该发生(这就是为什么它们没有被检查),但如果它们发生了,那么仍有例外。

因此,请抛出原始RuntimeException作为原因的Exception

catch (UnsupportedEncodingException e) {
    throw new RuntimeException(e); //should not happen
}

assert(false);也会抛出一个未经检查的异常,但它的断言可以关闭,所以我建议使用RuntimeException。