【问题标题】:Why Java StringReader throws IOException?为什么 Java StringReader 会抛出 IOException?
【发布时间】:2014-04-17 07:24:44
【问题描述】:

今天我正在使用 Java 类 StringReader,我发现它在 read 方法上抛出 IOException 非常烦人。我知道它扩展了Reader 类,其中方法read 抛出IOException,但我认为StringReader 不需要它。此类不使用任何可能导致错误的外部资源。

经过短暂调查,我发现StringReader#read 抛出 IOException 如果此类读取的字符串为 null 但事实上这 不可能发生,因为如果我们尝试将 null 传递给 @ 987654327@构造函数它抛出NPE。

你怎么看,总是抛出与超类相同的异常是一种好习惯吗?


编辑: 正如 U Mad Reader 所指出的,它是一个类而不是接口。

【问题讨论】:

  • StringReader 中的 throws 声明是强制性的,因为接口 Reader 需要它,即使实现类永远不会抛出这样的异常。
  • 投反对票的原因是什么?
  • Reader 是一个类,而不是一个接口,您可以使用一个实现来覆盖一个函数,而声明的异常更少或没有。它会起作用。所以 StringReader 没有理由声明它们。
  • @UMad,这是真的,但 Reader 是抽象的,读取的方法来自 Readable 接口。
  • 没关系。 StringReader 的制造者可以在没有 IOException 的情况下声明 read。这是他们的一个错误。

标签: java api stringreader


【解决方案1】:

如果你的实现确保它永远不会发生,我认为抛出与超类或接口定义相同的异常不是一个好习惯。我总是会将签名减少到所需的最低限度。

所有可以想象的实现都需要IOException,包括文件源、流和套接字等。否则,此类实现无法将其错误通知为已检查异常。但是如果一个实现不需要抛出一个检查异常(这对于调用代码来说通常很烦人),从实现类中删除它并没有什么害处,但会减轻一些负担。

更新:

我找到了 read() 方法 必须 抛出 IOException: 的原因,因为为 close() 方法定义的合同。来自 JavaDoc:

关闭流并释放与其关联的所有系统资源。关闭流后,进一步的 read()、ready()、mark() 或 reset() 调用将引发 IOException。关闭之前关闭的流没有任何效果。

【讨论】:

  • 哈哈,检查异常的冗长世界:-)
  • 你不能在这里做。
  • @Braj:不知道怎么了? ensureOpen() 没有解释为什么必须抛出 IOException。仅当您将代码进一步跟踪到 close() 方法时,您才能找到真正的原因。此外:它是一个回答问题而不是赢得价格的平台。所以保持冷静:-)
  • 它实际上是 ensureOpen() 抛出它的方法。然后我研究了如何将this.str分配给null然后我来close()方法。
【解决方案2】:

请查看StringReader#read()

查看StringReader#read()方法的源码。它调用ensureOpen() 方法实际上是在抛出IOException,因为ensureOpen() 检查以确保流尚未关闭。

如果关闭阅读器,然后在再次调用read() 之后会发生什么?

直接来自以上链接的源代码(查看 cmets):

/**
 * Reads a single character.
 *
 * @return     The character read, or -1 if the end of the stream has been
 *             reached
 *
 * @exception  IOException  If an I/O error occurs
 */
public int read() throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (next >= length)
            return -1;
        return str.charAt(next++);
    }
}

/** Check to make sure that the stream has not been closed */
private void ensureOpen() throws IOException {
    if (str == null)
        throw new IOException("Stream closed");
}

/**
 * Closes the stream and releases any system resources associated with
 * it. Once the stream has been closed, further read(),
 * ready(), mark(), or reset() invocations will throw an IOException.
 * Closing a previously closed stream has no effect.
 */
public void close() {
    str = null;
}

【讨论】:

  • 我在问问题之前做到了:)。我发现 ensureOpen 检查输入字符串是否不为空,这不会发生。
  • 但是为了安全起见,Java 程序员专家已经进行了这项检查。
  • 如果读者是closed,那么在再次调用read之后会发生什么?
  • 它可能会发生。我检查了构造函数,发现之前会抛出一个NullPointerException。但后来我搜索了整个代码,找到了 close() 方法,修改了 str 成员变量。阅读 JavaDoc 得出结论,read() 必须抛出 IOException。
  • @Harmlezz 这就是我在上一条评论中已经提出的问题。
【解决方案3】:

当您在类中实现接口方法时,不需要提供相同的异常参数。当您覆盖超类的方法声明时,同样的情况也适用。

public class MyReader implements Readable {

    @Override
    public int read(CharBuffer cb)  {
        return 0;
    }

}

但是你没有以正确的方式使用界面。如果您对接口进行编码,则不会从中受益。

Readable readable = new MyReader();

        try {
            readable.read(null);
        } catch (IOException e) {
            e.printStackTrace();
        }

即使在MyReader 中不要暴露IOException 你仍然必须使用try 块。因此,如果您不抛出您实现的方法的异常,可能会指出您在该方法的实现中遗漏了一些东西。所以恕我直言,这样做不是一个好习惯。

StringBuilder抛出IOException的原因并不是它实现了接口Readable。原因是验证方法ensureOpen() 中的输入,当输入为空时会抛出IOException。然后在调用方法close() 或将null 传递给构造函数时,输入可以为null。由于方法 close 是抽象的,它必须在子类中产生一些影响。预期的是,在您调用 close 后,您将无法再从中读取,您将获得 IOException。

这是考虑到所有潜在用例的完美、干净和可靠的实现。

【讨论】:

    猜你喜欢
    • 2021-03-05
    • 2012-01-13
    • 2012-09-21
    • 1970-01-01
    • 2013-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多