Обычно, когда вам нужно закрыть ресурс, который вы использовали в Java, вы обращаетесь к надежному блоку finally
и закрываете все открытые ресурсы внутри блока finally
.
Почему нужно всегда закрывать открытые ресурсы
Например, предположим, что вы используете объект java.sql.Connection
для подключения к базе данных, объект java.sql.PreparedStatement
для подготовки SQL-запроса и объект java.sql.ResultSet
для получения возвращаемого результата из запроса к базе данных.
Естественно, поскольку вы находитесь в идеальной зоне разума, вы должны помнить о закрытии объекта java.sql.Connection
, объекта java.sql.PreparedStatement
и объекта java.sql.ResultSet
в блоке finally
, как показано в приведенном ниже фрагменте кода.
Connection con; PreparedStatement ps = null; ResultSet rs = null; try { // your code with database connections } catch ( Exception e ) { //Catch and handle any exceptions } finally { con.close(); ps.close(); rs.close(); }
Примечание. Закрытие объекта
java.sql.ResultSet
не требуется явно (поскольку объект java.sql.ResultSet
автоматически закрывается сгенерировавшим его объектом Statement, когда этот объектStatement
закрывается, повторно выполняется или используется для извлечения следующего результата из последовательности нескольких результатов согласно документации Java оjava.sql.ResultSet
) рекомендуется закрывать объектResultSet
вручную, потому что некоторые драйверы JDBC имеют тенденцию к сбою при автоматическом закрытии объектаResultSet
.
Но что произойдет, если вы забудете закрыть одно из соединений с базой данных, которое вы открыли и использовали? Ваше приложение будет держать соединение с базой данных открытым и предотвратит открытие любых новых соединений, если превышен предел количества открытых соединений с базой данных. Это может привести к критическим сбоям в вашем приложении и к очень трудным для определения ошибкам в вашей системе.
Здесь на помощь придет новый оператор try-with-resources, представленный в Java 7.
Использование try-with-resources в основном таково: вы можете объявить все закрываемые объекты ресурсов, которые вы собираетесь использовать, внутри предложения try, и Java позаботится о закрытии ресурсов самостоятельно.
try ( Connection con = getConnection(); PreparedStatement ps = null; ResultSet resultSet = null; ) { // your code with database connections } catch ( Exception e ) { //Catch and handle any exceptions } finally { // Instead of closing open resources, you can do anything else in here }
Обратите внимание, что с оператором try-with-resources можно использовать только объекты, реализующие java.lang.AutoCloseable
. Сюда входят все классы, реализующие java.io.Closeable
.
И еще кое-что
Еще одним преимуществом использования инструкции try-with-resources является возможность предотвратить маскирование исключений.
Возьмем следующий пример.
У нас есть myMethod()
с обычным блоком try/finally для обработки объектов ресурсов. И у нас есть метод main
, который перехватывает исключение, выдаваемое myMethod()
.
В myMethod()
есть два исключения, которые могут быть выброшены.
1. ExceptionType1 выбрасывается в блоке try.
2. ExceptionType2 выдается при попытке закрыть ресурс.
public static void myMethod() throws Exception() { Closeable resource = null; try { // your code which throws ExceptionType1 } finally { resource.close(); // an exception of type ExceptionType1 will be thrown when trying to close the resource } }
И пример, где вы используете вышеуказанный метод.
public static void main(String[] args) { try { myMethod(); } catch( Exception e) { e.printStackTrace(); } }
В приведенном выше сценарии, когда исключение генерируется в блоке try
и в блоке finally
, блок catch
получит только исключение ExceptionType2. То есть ExceptionType1, сгенерированный первым, будет маскирован с помощью ExceptionType2, сгенерированного вторым.
Итак, как нам идентифицировать исключение, вызванное нашим блоком кода?
Здесь нам на помощь приходит оператор try-with-resources. Давайте проанализируем приведенный выше сценарий, используя оператор try-with-resource.
Основное исключение ExceptionType1, которое нам нужно, сначала выбрасывается блоком кода. Затем возникает второе исключение ExceptionType2, когда Java пытается закрыть ресурсы.
Но разница в том, что второе исключение будет подавлено и добавлено к основному исключению ExceptionType1 как подавленноеисключение. представлены в блок catch
метода main()
.
То есть, если мы структурируем наш myMethod()
, как показано ниже, исключение, пойманное в блоке catch
, будет ExceptionType1 с ExceptionType2, добавленным как подавленное исключение. Таким образом, мы поймаем нужное нам исключение, не маскируя его более поздним исключением.
public static void myMethod() throws Exception() { try ( Closeable resource = null; ) { // your code which throws ExceptionType1 } }
Заключение
Используя в своем коде операторы try-with-resources для управления ресурсами, вы получите безопасный и удобный для чтения код, который также может интуитивно обрабатывать исключения.
Дополнительные сведения об использовании инструкции try-with-resources и о том, как обрабатываются исключения внутри инструкции try-with-resources, см. на странице