Является ли блокfinally без блока catch анти-шаблоном Java?

StackOverflow https://stackoverflow.com/questions/601152

Вопрос

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

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

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

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

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

Итак, мой вопрос заключается в следующем:

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

Это было полезно?

Решение

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

Другие советы

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

Нисколько.

Что не так, так это код внутри файлаfinally.

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

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

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 без catch.

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

Когда я имею дело с дескрипторами файлов (для записи), я очищаю поток перед его закрытием с помощью метода 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);
}

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

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

Я бы сказал, что блок try без блока catch — это антишаблон.Высказывание «Не делай наконец-то без подвоха» — это разновидность фразы «Не пытайся без подвоха».

Я использую 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).Я думаю, что это более лаконично, и я не дублирую улов (исключение).

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

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

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

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

acquire
try {
    use
} finally {
    release
}

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

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

Поэтому абстрагируйте обработку ресурсов с помощью идиомы «Выполнение вокруг».

Try/Finally — это способ правильно освободить ресурсы.Код в блоке Final НИКОГДА не должен выдавать исключение, поскольку он должен воздействовать только на ресурсы или состояние, которые были получены ДО входа в блок try.

Кроме того, я думаю, что log4J почти антипаттерн.

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

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

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 пункт.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top