Разберите фрагмент на Java: "class A{ void m(){ try{ return; } finally { doCleanup(); } } }" — как работает finally при возвращении значения и какие практические последствия для ресурсов и исключений
Коротко — что делает finally в вашем примере - Выполнение идет в try, `return;` подготавливает возврат, но перед фактическим выходом JVM всегда выполняет блок `finally`. В вашем коде `doCleanup();` выполнится всегда (если только процесс не завершён принудительно, например `System.exit()` или kill). - После выполнения `finally` метод реально возвращается (в данном примере — возврат `void`). Порядок при возвращении значения (не-void) - Выражение возврата вычисляется и его значение сохраняется в скрытой временной переменной. - Затем выполняется `finally`. - Если `finally` не выбросил исключение и не сделал `return`, из метода возвращается ранее сохранённое значение. - Если `finally` содержит `return`, оно заменяет прежний подготовленный возврат. - Если `finally` выбрасывает исключение, оно заменяет прежний возврат или исключение из `try`. Примеры эффектов - Изменение состояния возвращаемого объекта в `finally` видно вызывающему: class A{ Object m(){ Foo f = new Foo(); return f; } finally { f.field = 1; } } — вызывающий увидит field==1. - Если `finally` сделает `return` — прежнее возвращённое значение игнорируется. - Если и в `try` и в `finally` выброшены исключения, наружу пойдет исключение из `finally`; исходное теряется (вручную можно сохранить/добавить как suppressed, но в обычном try-finally этого не делается). Взаимодействие с ресурсами и исключениями — практические рекомендации - Блок `finally` хорош для очистки (закрыть файлы, освобождение) — он обычно гарантирует выполнение очистки. - Предпочитайте конструкцию try-with-resources (с Java 777) для закрытия ресурсов — она аккуратно добавляет suppressed-исключения и чище по коду. - Избегайте `return` в `finally` — он скрывает предыдущие возвраты и исключения, что приводит к трудноуловимым ошибкам. - Избегайте бросать исключения из `finally`, если это можно предотвратить — они перезаписывают полезную информацию об исходной ошибке. - Если в `finally` есть риск исключения, перехватите его и, при необходимости, добавьте как suppressed к первоначальному исключению вместо того, чтобы позволить ему затереть основное. Краткий вывод - В вашем фрагменте `doCleanup()` выполнится всегда перед выходом. Но будьте осторожны: `finally` может изменить/скрыть возврат или исключение — лучше использовать try-with-resources и не делать `return`/throw в `finally`.
- Выполнение идет в try, `return;` подготавливает возврат, но перед фактическим выходом JVM всегда выполняет блок `finally`. В вашем коде `doCleanup();` выполнится всегда (если только процесс не завершён принудительно, например `System.exit()` или kill).
- После выполнения `finally` метод реально возвращается (в данном примере — возврат `void`).
Порядок при возвращении значения (не-void)
- Выражение возврата вычисляется и его значение сохраняется в скрытой временной переменной.
- Затем выполняется `finally`.
- Если `finally` не выбросил исключение и не сделал `return`, из метода возвращается ранее сохранённое значение.
- Если `finally` содержит `return`, оно заменяет прежний подготовленный возврат.
- Если `finally` выбрасывает исключение, оно заменяет прежний возврат или исключение из `try`.
Примеры эффектов
- Изменение состояния возвращаемого объекта в `finally` видно вызывающему:
class A{ Object m(){ Foo f = new Foo(); return f; } finally { f.field = 1; } } — вызывающий увидит field==1.
- Если `finally` сделает `return` — прежнее возвращённое значение игнорируется.
- Если и в `try` и в `finally` выброшены исключения, наружу пойдет исключение из `finally`; исходное теряется (вручную можно сохранить/добавить как suppressed, но в обычном try-finally этого не делается).
Взаимодействие с ресурсами и исключениями — практические рекомендации
- Блок `finally` хорош для очистки (закрыть файлы, освобождение) — он обычно гарантирует выполнение очистки.
- Предпочитайте конструкцию try-with-resources (с Java 777) для закрытия ресурсов — она аккуратно добавляет suppressed-исключения и чище по коду.
- Избегайте `return` в `finally` — он скрывает предыдущие возвраты и исключения, что приводит к трудноуловимым ошибкам.
- Избегайте бросать исключения из `finally`, если это можно предотвратить — они перезаписывают полезную информацию об исходной ошибке.
- Если в `finally` есть риск исключения, перехватите его и, при необходимости, добавьте как suppressed к первоначальному исключению вместо того, чтобы позволить ему затереть основное.
Краткий вывод
- В вашем фрагменте `doCleanup()` выполнится всегда перед выходом. Но будьте осторожны: `finally` может изменить/скрыть возврат или исключение — лучше использовать try-with-resources и не делать `return`/throw в `finally`.