Правила создания исключений Java «наконец-то»

Допустим, у меня есть класс с двумя членами OutputStream:

public class A {
    OutputStream os1 = ...
    OutputStream os2 = ...

    ...
}

Теперь в этом классе у меня есть метод cleanup(), который должен как минимум попытаться закрыть оба потока:

public void cleanup() throws IOException {
    try {
        os1.close();
    } finally {
        os2.close();
    }
}

Теперь я уверен, что приведенный выше код попытается закрыть оба потока, но интересно, что происходит, когда оба вызовы close() вызывают исключение IOException. Я хочу сказать, что исключение из os2 будет распространяться вверх, но, возможно, поведение не определено. Я немного погуглил, но не нашел ничего, что бы хорошо отвечало на этот вопрос.


person Ben    schedule 23.05.2014    source источник
comment
Подобный метод очистки — это запах кода. Вместо этого вы должны использовать try-with-resources.   -  person Raedwald    schedule 13.05.2019


Ответы (3)


JLS §14.20.2 описывает поведение. Исключение, выдаваемое методом, — это исключение, вызванное os2.close().

person fabian    schedule 24.05.2014

Если finally выдает исключение, то метод завершается с этим исключением. Это очень четко определенное поведение. Аналогично, если finally возвращается, то метод возвращается с возвращаемым значением finally, независимо от любого предыдущего возврата из связанного блока try.

person Ernest Friedman-Hill    schedule 24.05.2014

Да, второе исключение распространяется вверх, а первое исключение теряется.

Но если вы используете Java 7 или более позднюю версию, гораздо лучше использовать синтаксис «попробовать с ресурсами». Код намного чище, его нужно писать намного меньше, и он гораздо лучше, чем вы, обеспечивает закрытие ресурсов в нужное время, несмотря ни на что. Например, как бы умно и красиво вы ни делали cleanup(), если он не вызывается, значит, вам не повезло. Что, если контекст, который должен вызывать его сам, выдает исключение? Не делай так!

person Alvin Thompson    schedule 24.05.2014
comment
Не все ресурсы инициализируются и очищаются в одной и той же области. Таким образом, вы не можете использовать синтаксис попытки с ресурсами, если вы инициализируете поток в конструкторе класса и закрываете его в методе. - person E_net4 the curator; 24.05.2014
comment
@ E_net4 - Если вы покажете мне код, в котором ресурс не инициализируется и не очищается в той же области, я покажу вам код, который можно было бы написать лучше. - person Alvin Thompson; 24.05.2014
comment
Возможно, термин «сфера действия» здесь стал запутанным. Но пример кода не нужен, просто прочитайте и обдумайте мой предыдущий комментарий. - person E_net4 the curator; 24.05.2014
comment
@ E_net4 - Нет путаницы. Я с уважением не согласен. И вы действительно пытаетесь доказать, что постер не должен хотя бы подумать о переписывании, чтобы использовать синтаксис попытки с ресурсами? - person Alvin Thompson; 24.05.2014
comment
Что ж, я хочу сказать, что оператор try-with resources не может начинаться с одного метода и заканчиваться другим. Он находится в той же области видимости метода, инициализируется и закрывается при каждом вызове метода. В примере OP мы используем ресурсы, которые предназначены для использования не дольше, чем время жизни объекта. Мы не можем этого гарантировать. Однако мы можем сделать так, чтобы они поддерживали функцию try-with. pastebin.com/yeYZ74Vj - person E_net4 the curator; 24.05.2014
comment
@ E_net4 - Во-первых, никто не сказал, что оператор try-with resources может начинаться с одного метода и заканчиваться другим. Во-вторых, я не согласен с вашим первоначальным утверждением о том, что вы не можете использовать синтаксис «попробовать с ресурсами», если вы инициализируете поток в конструкторе класса и закрываете его в методе, потому что это явно неверно. Даже код, который вы только что предоставили, доказывает, что это неправда. try (AnimalPrinter ap = new AnimalPrinter("foo")) { ... } - person Alvin Thompson; 24.05.2014
comment
У нас слишком долгое время возникло явное недопонимание, так что позвольте мне сделать последний комментарий. Действительно, вы можете сделать try (new AnimalPrinter("foo")) { ... }, потому что я сделал это Closeable. Это ожидаемо. Но обратите внимание, как я не мог не написать os.close() в реализации AnimalPrinter. вы не можете использовать синтаксис «попробовать с ресурсами», если вы инициализируете поток в конструкторе класса и закрываете его в методе, потому что это будет расширять оператор попытки через несколько реализаций метода, что не имеет смысла. Надеюсь, теперь все ясно, это даже не поможет ОП. - person E_net4 the curator; 24.05.2014
comment
@ E_net4 - Значит, весь ваш смысл был в том, чтобы сказать: если вы не напишете код правильно, он не будет работать? Что ж, это очевидно, но спасибо за вклад. - person Alvin Thompson; 24.05.2014