Java:写入 ByteArrayOutputStream 时出现 IOException?

12
由于ByteArrayOutputStream仅写入内存,因此不应发生IOException。但是,由于OutputStream接口的合同,所有流操作都在其throws子句中定义IOException
正确的处理从未发生的IOException的方法是什么?只需将操作包装在空的try-catch块中吗?
或者有任何实际情况下ByteArrayOutputStream可能会引发异常吗?
(另请参见:如何以安全和可读的方式处理我知道永远不会抛出的IOException?
编辑
正如Jon指出的那样,ByteArrayOutputStream没有在其定义的write方法上声明throws子句--但是,它继承了OutputStreamwrite(byte[]),并且其中确实会抛出IOEXception(非常奇怪,BAOS不会覆盖此方法,因为它可以用更高效的arraycopy调用替换超类版本- -它每次写入一个字节)
6个回答

12

ByteArrayOutputStream并没有声明任何方法会抛出IOException,除了writeToclose。(说实话,我不知道为什么close还会声明它。)

但是,如果你有一个类型为OutputStream的引用,当然仍然会看到该类型的throws声明。

我不会使用空的catch块——我会抛出类似于IllegalStateException或类似的未经检查的异常:这意味着你处于一个你真的没想到的情况下,而且出现了严重的错误。


close() 上的 IOException 必定是一个错误 - 特别是当 javadoc 表明它“没有任何影响”时。 - irreputable
1
+1 谢谢!我刚刚注意到 ByteArrayOutputStream.write 实际上没有声明 IOException,但是每当我使用它时,Eclipse 都会抱怨未处理的异常……很奇怪。 - Tony the Pony
@irreputable:这不是错误。仅仅因为“InputStream”什么也没做,并不意味着具体的子类也什么都没做。它们在释放流之前可能会刷新(而且很多子类确实会这样做)。 “InputStream”声明了契约,这个契约允许在关闭流时出现错误。 - musiKk
1
@Jon:我正在从ByteArrayOutputStream的子类中调用write(byte[])BAOS没有覆盖此方法,因此我实际上是在调用OutputStream中定义的方法(它会抛出一个IOE)。 - Tony the Pony
1
@Jen:啊,好的。那很有道理。你可以自己调用write(data, 0, data.length) - Jon Skeet
显示剩余6条评论

4
自Java 11开始,ByteArrayOutputStream.writeBytes(byte[])新增了一个方法,也不会抛出IOException异常:
/**
 * Writes the complete contents of the specified byte array
 * to this {@code ByteArrayOutputStream}.
 *
 * ...
 *
 * @since   11
 */
public void writeBytes(byte b[]) {
    write(b, 0, b.length);
}

如果您不想处理从未被抛出的 IOException,可以使用此方法。

在Java 8中,我可以看到一个名为write(byte b[], int off, int len)的方法,这也是新引入的语法所调用的。重点是,现有的方法也没有抛出IOException。 - Naman

2
我注意到 ByteArrayOutputStream.write 实际上并没有声明 IOException,但是每当我使用它时,Eclipse 都会抱怨有未处理的异常…很奇怪。
很容易解释。你可能做了这样的事情:
    OutputStream os = new ByteArrayOutputStream();
    ...
    os.write();

“问题”在于您调用的方法是OutputStream.write()而不是ByteArrayOutputStream.write()。因此编译器会提示:

“啊... OutputStream上的write()可能会抛出IOException,所以你必须处理它。”

它无法说:

“这个特定的OutputStream实际上是一个ByteArrayOutputStream...所以我们放过你吧。”

因为JLS不允许这样做。

这是一种遵循“最佳实践”,通过编写接口而不是实现类的代码来回击您的边缘情况之一。

好的,那么...

  • 这只是轻微的咬,而不是全面的咬。
  • OutputStream作为Java类而不是Java接口实现,但这并不重要。
  • 大多数编译器在编译代码时实际上并不与您交谈 :-)

总的来说,你是正确的。然而,ByteArrayOutputStream 愚蠢地重新声明了 close() 方法,什么也不做并抛出 IOException 异常,因此在这种情况下,引用类型并不重要(OpenJDK)。 - musiKk
谢谢,解释得非常好。我明白了发生了什么。我正在从ByteArrayOutputStream的子类中调用write(byte[])BAOS没有覆盖此方法,因此实际上是在调用OutputStream中定义的方法(它会抛出一个IOE)。 - Tony the Pony

1
一个典型的陈词滥调是在 catch 块中 throw new RuntimeException(theIOException)。如果不可能发生的事情确实发生了,你至少会知道它发生了。

1

在这种情况下,异常链是最佳实践。即抛出一个RuntimeException。


1

自 2002 年以来,针对此问题有一个 ticket 的增强版本。之所以没有修复这个问题,是因为这会影响与先前的 Java 版本的兼容性。

以下是我考虑的两种解决方法。

解决方法 1

write(byte[], int, int) 方法不会抛出已检查异常。 指定另外两个参数会略微冗长。但总体上,没有 try-catch 的占用空间更小。

baos.write(array, 0, array.length);

解决方法2

另一个可能的解决方法是编写自己的ByteUtil,在内部捕获异常。

public final class ByteUtil
{
  public static void write(ByteArrayOutputStream baos, byte[] bytes)
  {
    try
    {
      baos.write(bytes);
    }
    catch (IOException e)
    {
      // impossible
    }
  }
}

// usage
ByteUtil.write(baos, bytes);

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