Подтвердить что ты не робот

Является ли блок finally без блока catch java anti-pattern?

У меня был довольно болезненный опыт устранения неполадок при поиске и устранении некоторых ошибок, которые выглядели следующим образом:

try {
   doSomeStuff()
   doMore()
} finally {
   doSomeOtherStuff()
}

Проблема была сложной для устранения неполадок, поскольку doSomeStuff() выбрасывал исключение, которое, в свою очередь, вызывало doSomeOtherStuff(), чтобы также генерировать исключение. Второе исключение (брошенное блоком finally) было передано моему коду, но у него не было дескриптора первого исключения (выбрано из doSomeStuff()), что стало основной причиной проблемы.

Если бы этот код сказал это, проблема была бы очевидна:

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

Итак, мой вопрос таков:

Является ли блок finally используемым без блокировки catch известным java-анти-шаблоном? (Это, безусловно, кажется не совсем понятным подклассом явно известного анти-шаблона "Не усваивайте исключения!" )

4b9b3361

Ответ 1

В общем, нет, это не анти-шаблон. Пункт блоков finally состоит в том, чтобы убедиться, что материал очищается от того, выбрано ли исключение или нет. Весь смысл обработки исключений заключается в том, что, если вы не можете с этим справиться, вы позволяете ему пузыриться до тех, кто может, благодаря относительно чистой внеполосной обработке исключений сигнализации. Если вам нужно убедиться, что материал очищается, если вызывается исключение, но не может правильно обработать исключение в текущей области, то это именно то, что нужно сделать. Возможно, вам захочется немного позаботиться о том, чтобы убедиться, что ваш блок finally не бросается.

Ответ 2

Я думаю, что настоящий "анти-шаблон" здесь делает что-то в блоке finally, который может бросать, не имея уловки.

Ответ 3

Совсем нет.

В чем ошибка кода внутри.

Помните, что в конечном итоге вы всегда будете исполняться и просто рискованно (как вы только что засвидетельствовали) поставить что-то, что может вызвать там исключение.

Ответ 4

Нет абсолютно ничего плохого в попытке с окончанием и без улова. Рассмотрим следующее:

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

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

Ответ 5

Я думаю, что это далеко не анти-шаблон, и это то, что я делаю очень часто, когда он критично освобождает ресурсы, полученные во время выполнения метода.

Одна вещь, которую я делаю при работе с файловыми дескрипторами (для записи), - это очистка потока перед его закрытием с помощью метода IOUtils.closeQuietly, который не генерирует исключений:


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

Мне нравится делать это по следующим причинам:

  • Нельзя полностью игнорировать исключение при закрытии файла - если есть еще байты, которые еще не были записаны в файл, тогда файл может не находиться в состоянии, которое ожидающий вызывал бы;
  • Итак, если исключение возникает во время метода flush(), оно будет передано вызывающему, но я все равно сделаю все файлы закрытыми. Метод IOUtils.closeQuietly(...) менее подробный, тогда соответствующий try... catch... ignore me block;
  • При использовании нескольких выходных потоков очень важен порядок для метода flush(). Потоки, созданные при передаче других потоков в качестве конструкторов, должны сначала очищаться. То же самое справедливо для метода close(), но flush() более ясен, на мой взгляд.

Ответ 6

Я использую try/finally в следующей форме:

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

Я предпочитаю это для try/catch/finally (+ вложенный try/catch в finally). Я думаю, что он более краткий, и я не дублирую catch (Exception).

Ответ 7

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

Не делайте этого... вы просто спрятали больше ошибок (ну не совсем спрятали их... но затруднились с ними разобраться. Когда вы поймаете Exception, вы также поймаете любое исключение RuntimeException (например, NullPointer и ArrayIndexOutOfBounds).

В общем, поймайте исключения, которые вы должны поймать (проверенные исключения), и общайтесь с другими во время тестирования. RuntimeExceptions предназначены для использования для ошибок программиста, а ошибки программиста - это вещи, которые не должны выполняться в правильно отлаженной программе.

Ответ 8

На мой взгляд, это скорее случай, когда finally с a catch указывает на какую-то проблему. Идиома ресурса очень проста:

acquire
try {
    use
} finally {
    release
}

В Java у вас может быть исключение практически из любого места. Часто приобретатель бросает проверенное исключение, разумный способ справиться с этим - это улов вокруг того, как много. Не пытайтесь устранить нулевую проверку.

Если бы вы были действительно анальным, вы должны отметить, что среди исключений есть определенные приоритеты. Например, ThreadDeath должен сжимать все, независимо от того, происходит ли это от получения/использования/выпуска. Правильное управление этими приоритетами неприглядно.

Поэтому, абстрагируйте обработку ваших ресурсов с помощью идиомы Execute Around.

Ответ 9

Я бы сказал, что блок try без блока catch является анти-шаблоном. Высказывание "Не иметь окончательно без улова" - это подмножество "Не пытайтесь без улова".

Ответ 10

Попробуйте/Наконец, это способ правильно освободить ресурсы. Код в блоке finally никогда не должен бросаться, поскольку он должен действовать только на ресурсы или состояние, которое было получено PRIOR для входа в блок try.

В стороне, я думаю, log4J почти анти-шаблон.

ЕСЛИ ВЫ ХОТИТЕ ПРОВЕРИТЬ ПРОГРАММУ РАБОТЫ ИСПОЛЬЗУЙТЕ ИНСТРУКЦИЮ ПО ПРОВЕРКЕ ИНСПЕКЦИИ (то есть отладчик, IDE или в крайнем смысле - перекодировщик байтового кода, но НЕ ЗАПУСКАЙТЕ СООБЩЕНИЯ В КАЖДОЙ НЕСКОЛЬКО ЛИНИИ!).

В двух примерах, которые вы представляете, первый выглядит правильно. Второй - код регистратора и вводит ошибку. Во втором примере вы подавляете исключение, если вы бросаете первые два утверждения (т.е. Вы его ломаете и регистрируете, но не ревертируете). Это то, что я нахожу очень распространенным в использовании log4j и представляет собой настоящую проблему разработки приложения. с вашим изменением вы отказываетесь от программы таким образом, чтобы система обрабатывалась очень тяжело, так как вы в основном маршируете вперед, как если бы у вас никогда не было исключения (вроде как VB basic при ошибке возобновить следующую конструкцию).

Ответ 11

try-finally может помочь вам уменьшить код копирования-вставки, если метод имеет несколько операторов return. Рассмотрим следующий пример (Android Java):

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) {
    Cursor cursor = db.rawQuery("SELECT * FROM table", null);
    if (cursor != null) { 
        try {
            if (cursor.getCount() == 0) { 
                return false;
            }
        } finally {
            // this will get executed even if return was executed above
            cursor.close();
        }
    }
    // database had rows, so do something...
    return true;
}

Если не было предложения finally, вам может потребоваться дважды написать cursor.close(): перед return false, а также после окружающего предложения if.

Ответ 12

Я думаю, что попытка без улова - это анти-шаблон. Использование try/catch для обработки исключительных условий (ошибки ввода-вывода файла, тайм-аут сокета и т.д.) Не является анти-шаблоном.

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