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

Наконец, иногда меня путают

Сегодня в колледже мы немного поговорили о try, catch и finally. Я запутался в этих двух примерах:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
} finally { // And we close it here
  out.close();
}

В чем разница между закрытием файла в finally и если мы просто сделали это следующим образом:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
}
out.close();

Этот фрагмент кода после catch всегда будет выполняться.

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

4b9b3361

Ответ 1

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

Во-вторых, если по какой-либо причине e.printStackTrace() выдает исключение (хотя это было бы очень редко), то же самое произойдет - finally будет выполняться.

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

try (PrintWriter out = new PrintWriter(...)) {
    // do whatever with out
}
catch (Exception e) {
    e.print... (whatever)
}
// no need to do anything else, close is invoked automatically by try block

EDIT: Также обратите внимание, что ваш код не совсем прав (независимо от того, какая версия). Если конструктор PrintWriter генерирует исключение, строка out.close() завершится с ошибкой NullPointerException.

Ответ 2

Если в блоке try возникает непрозрачная ошибка или даже если в вашем блоке catch возникает ошибка, "фрагмент кода" после улова не будет выполнен, но блок finally.

finally всегда будет выполняться.

Из документации Java:

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

Ответ 3

Что, если что-то в блоке catch будет выбрасывать Exception? out.close не будет выполняться. Вы также можете использовать попробовать с ресурсами", чтобы убедиться, что все ресурсы закрыты после его использования. Попробуйте этот пример:

public static void withFinnaly() {
        try {
            throwException();
            System.out.println("This won't execute");
        } catch (Exception e) {
            System.out.println("Exception is caught");
            throwException();
        } finally {
            System.out.println("Finally is always executed," +
                    " even if method in catch block throwed Exception");
        }
    }

    public static void withOutFinnaly() {
        try {
            throwException();
            System.out.println("This won't execute");
        } catch (Exception e) {
            System.out.println("Exception is caught");
            throwException();
        }
        System.out.println("Looks like we've lost this... " +
                "This wont execute");
    }

    public static void throwException() throws RuntimeException {
        throw new RuntimeException();
    }

Ответ 4

Обычная usecase, наконец, заключается в том, что вы не хотите поймать исключение в том же методе.

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

Ответ 5

В Java исходный код:

void foo()
{
  try {
    if (W())
      return;
  }
  catch (FooException ex) {
    if (X())
      throw;
  }
  finally {
    Y();
  }
  Z();
}

будет преобразован компилятором в:

void foo()
{
  try {
    if (W()) {
      Y();
      return;
    }
  }
  catch (FooException ex) {
    if (X()) {
      Y();
      throw;
    }
  }
  catch {
    Y();
    throw;
  }
  Y();
  Z();
}

Эффект состоит в том, чтобы вызвать дублирование кода в блоке finally все места, где контроль может оставить метод. Любой блок try который имеет finally, но не обработчик catch-all, эквивалентен одному с обработчиком catch-all, который сразу же бросает (при котором процессор может затем вставить копию кода finally перед обработчиком catch-all.

Ответ 6

Второй пример может вызвать нежелательный NullPointerException, если в конструкторе PrintWriter возникает исключение.

Другая возможность заключается в том, что out.close(); выдаст ошибку, которая не была поймана.

Если вы переместите код в блок finally, он будет всегда выполняться - независимо от того, успешный или успешный блок try. Это особенно полезно, если ваш try -block вызывает исключение, которое не было обнаружено. В вашем втором примере это приведет к тому, что out.close() не будет выполняться, тогда как с блоком finally он будет выполнен, даже если блок try выдает нечеткую ошибку.

Ответ 7

Хотя не полные ответы сами, эти два примера использования try-finally (mis) могут быть полезными:

public class JavaApplication3 
{
    static int foo()
    {
        try
        {
            return 6;
        }
        finally
        {
            return 4;
        }

    }

    public static void main(String[] args)
    {
        System.out.println("foo: " + foo());
    }
}


public class JavaApplication3 
{
    static int foo()
    {
        try
        {
            throw new Exception();
        }
        finally
        {
            return 4;
        }

    }
    public static void main(String[] args)
    {
        System.out.println("foo: " + foo());
    }
}

Оба выхода программ 4.

Причину этого можно найти в Глава 14.20.2 JLS

Оператор try с блоком finally выполняется первым выполнением блока try. Тогда есть выбор:

• Если выполнение блока try завершается внезапно из-за выброса значения V, тогда есть выбор:
[...]
- Если тип времени выполнения V не является совместимым с уловимым класс исключения любого предложения catch в заявлении try, тогда, наконец, блок выполняется. Тогда есть выбор:
[...]
> Если блок finally окончательно завершится по причине S, то оператор try завершается внезапно по причине S (а выброс значения V отбрасывается и забыл).

Если выполнение блока try завершается внезапно по любой другой причине R, тогда выполняется блок finally, а затем есть выбор:
- Если окончательный блок завершается нормально, то инструкция try завершается внезапно по причине R.
- Если блок finally окончательно завершится по причине S, то оператор try завершается внезапно по причине S (и причина R отбрасывается).

Редактирование шахты

Считаем, что a return является неожиданным способом завершения блока try или finally.

Ответ 8

Меньший пример:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} finally {
    out.close();
}

Здесь мы не поймаем никаких исключений (обработанных вызывающим), но мы хотим, чтобы close писатель ли мы

  • работают нормально через блок try или
  • оставить исключение.

В обоих случаях код в finally выполняется как часть выхода из блока.


Здесь мы обнаруживаем подмножество исключений:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} catch (IOException e) {
    log(e);
} finally {
    out.close();
}
do_something_else();

Здесь есть три возможных пути:

  • нормальное выполнение части try, за которой следует finally, а затем do_something_else(),
  • фрагмент try throws IOException, который пойман и зарегистрирован, затем выполняется блок finally, а затем do_something_else() или
  • часть try выдает другой забрасываемый, который не пойман, но выполняется блок finally, затем код переходит к следующему заключению try, возможно, в вызывающем.

Соблюдайте осторожность при написании блока finally - он не находится внутри try, поэтому любые исключения там будут предотвращать любое исключение, которое выполняется. Короткий совет - попытаться избежать вещей, которые могут быть выбраны изнутри finally или catch.