Java: IOException при записи в ByteArrayOutputStream?

Поскольку ByteArrayOutputStream просто пишет в память, IOException никогда не должно возникать. Однако из-за контракта интерфейса OutputStream все потоковые операции определяют IOException в своем предложении throws.

Как правильно «обработать» этот никогда не встречающийся IOException? Просто обернуть операции в пустой блок try-catch?

Или есть реальные ситуации, когда ByteArrayOutputStream может вызвать исключение?

(См. также: -safe-and-r">Как я могу обработать IOException, которое, как я знаю, никогда не может быть вызвано, безопасным и читабельным способом?)

ИЗМЕНИТЬ

Как отмечает Джон, ByteArrayOutputStream не объявляет предложение throws для определяемых им методов write, однако он наследует write(byte[]) от OutputStream, и этот метод выдает IOEXception (весьма странно, что BAOS не перекрывает этот метод, поскольку он мог бы замените версию суперкласса, которая записывает один байт за раз, гораздо более эффективным вызовом arraycopy)


person Tony the Pony    schedule 07.06.2011    source источник


Ответы (6)


Что ж, ByteArrayOutputStream не объявляет, что любой из его методов выдает IOException, кроме writeTo и close. (Честно говоря, я не знаю, почему close до сих пор об этом заявляет.)

Однако, если у вас есть ссылка типа OutputStream, вы, конечно, все равно увидите объявления throws из нее.

Я бы не стал использовать пустой блок catch — я бы выдал что-то вроде IllegalStateException или похожее непроверенное исключение: это означает, что вы оказались в ситуации, которую вы на самом деле не ожидаете, и что-то пошло не так. .

person Jon Skeet    schedule 07.06.2011
comment
IOException для close() должно быть ошибкой, особенно когда javadoc говорит, что это не имеет никакого эффекта - person irreputable; 08.06.2011
comment
+1 Спасибо! Я только что заметил, что ByteArrayOutputStream.write на самом деле не объявляет IOException, но Eclipse жалуется на необработанное исключение всякий раз, когда я его использую... странно. - person Tony the Pony; 08.06.2011
comment
@Jen: Вы уверены, что вызываете методы для переменной, объявленной как ByteArrayOutputStream? - person Jon Skeet; 08.06.2011
comment
@irreputable: Это не ошибка. То, что InputStream ничего не делает, не означает, что конкретные подклассы ничего не делают. Они могут сбрасывать перед выпуском потока (и многие из них так и делают). InputStream объявляет контракт, и этот контракт допускает ошибки при закрытии потока. - person musiKk; 08.06.2011
comment
@Jon: я звоню write(byte[]) из подкласса ByteArrayOutputStream. BAOS не переопределяет этот метод, поэтому на самом деле я являюсь методом, определенным в OutputStream (который выдает IOE) - person Tony the Pony; 08.06.2011
comment
@Джен: Ах, верно. Это имеет смысл. Вы всегда можете просто позвонить write(data, 0, data.length) самостоятельно. - person Jon Skeet; 08.06.2011
comment
@Jon: Да, это лучший подход - действительно странно, что write(byte[]) не переопределяется... - person Tony the Pony; 08.06.2011
comment
@Jen: Ну, на самом деле у переопределения не будет другого кода - у него будет тот же код, что и у базового класса, только без объявления исключения. Какой-то странный случай :) - person Jon Skeet; 08.06.2011
comment
@Jon: Верно ... это дублировало бы вызов write(byte[],int,int), но, по крайней мере, это сохраняло бы согласованность API (бросание или не бросок) - person Tony the Pony; 08.06.2011
comment
@musiKk см. javadoc в ByteArrayOutputStream.close() - person irreputable; 08.06.2011
comment
@irreputable: А. Мой комментарий относился к InputStream. Исключение close в ByteArrayOutputStream действительно странное. - person musiKk; 08.06.2011

Я только что заметил, что ByteArrayOutputStream.write на самом деле не объявляет IOException, но Eclipse жалуется на необработанное исключение всякий раз, когда я его использую... странно.

Это легко объяснить. Вы, вероятно, сделали что-то вроде этого:

    OutputStream os = new ByteArrayOutputStream();
    ...
    os.write();

«Проблема» заключается в том, что вы вызываете метод как OutputStream.write(), а не как ByteArrayOutputStream.write(). Итак, компилятор говорит:

"Ах... write() на OutputStream может бросить IOException, так что с этим надо смириться."

Он не может сказать:

«Этот конкретный OutputStream на самом деле ByteArrayOutputStream… так что мы вас отпускаем».

потому что JLS не позволяет этого.

Это один из тех крайних случаев, когда следование «лучшей практике» путем кодирования интерфейса, а не класса реализации, возвращается к вам.

Хорошо так...

  • это нежный укус, а не полный укус.
  • OutputStream реализован как класс Java, а не как интерфейс Java, но это не имеет значения.
  • большинство компиляторов на самом деле не разговаривают с вами во время компиляции вашего кода :-)

person Stephen C    schedule 08.06.2011
comment
В целом вы правы. Однако ByteArrayOutputStream неразумно повторно объявляет close() ничего не делая и выбрасывая IOException, поэтому в этом случае ссылочный тип не имеет значения (OpenJDK). - person musiKk; 08.06.2011
comment
Спасибо, отличное объяснение. Я узнал, что происходит. Я вызываю write(byte[]) из подкласса ByteArrayOutputStream. BAOS не переопределяет этот метод, поэтому на самом деле я являюсь методом, определенным в OutputStream (который выдает IOE) - person Tony the Pony; 08.06.2011

Типичным клише является throw new RuntimeException(theIOException) в блоке catch. Если случится невозможное, ты хотя бы узнаешь об этом.

person bmargulies    schedule 07.06.2011

Цепочка исключений — лучшая практика в этой ситуации. то есть выбросить исключение RuntimeException.

person Basanth Roy    schedule 07.06.2011

С 2002 года существует улучшение для этой проблемы. Причина, по которой это не исправлено, это повлияет на совместимость с предыдущими версиями Java.

Ниже приведены 2 обходных пути, которые я бы рассмотрел.

Обходной путь 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);
person bvdb    schedule 07.06.2018

Начиная с 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, который никогда не выбрасывается.

person ZhekaKozlov    schedule 13.09.2018
comment
Я мог видеть в Java 8 метод write(byte b[], int off, int len), который то, что недавно введенный синтаксис также вызывает.. Суть в том, что существующий метод также также не вызывал исключение IOException.< /а> - person Naman; 14.09.2018