Работа с блоком finally

У меня есть код, который создает JarFile и URLClassLoader, оба из которых я хочу закрыть в конце. Естественно, для очистки я решил использовать блок finally:

JarFile jar = ...;
URLClassLoader loader = ...;
try {
    // work ...
} finally {
    jar.close();
    loader.close();
}

Однако оба вызова close() могут вызвать исключение, поэтому, если jar.close() вызовет исключение, то loader.close() не будет достигнуто. Один из способов обойти это — окружить jar.close() блоком try-catch:

JarFile jar = ...;
URLClassLoader loader = ...;
try {
    // work ...
} finally {
    try {
        jar.close();
    } catch(IOException e) {
    } 
    loader.close();
}

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


person Martin Tuskevicius    schedule 06.06.2015    source источник
comment
Разве вы не должны закрывать элементы в обратном порядке?   -  person Willem Van Onsem    schedule 06.06.2015


Ответы (3)


В Java 7 и более поздних версиях есть попытка с ресурсами, которые обрабатывают Closeable объекты.

Переформатируйте свой код таким образом,

try(JarFile jar = ....; URLClassLoader loader = ....;) 
{
    // work ...
}

Только классы, которые реализуют интерфейс Closeable, будут работать таким образом, оба этих класса соответствуют этому критерию.

person Patrick J Abare II    schedule 06.06.2015
comment
Очень приятно, я склонен скучать по этим новым функциям. Похоже, я проведу рефакторинг кода в активном проекте :) - person MalaKa; 06.06.2015
comment
Да, мне очень понравилась эта функция, я всегда использовал метод closeQuietly(Closeable... closeable), чтобы выполнить то же самое, что и ответ Дичи, но раньше допускал один вызов. - person Patrick J Abare II; 06.06.2015

Используйте try-with-resources:

    try (JarFile jar = new JarFile(filename);
            URLClassLoader loader = URLClassLoader.newInstance(urls)) {
        // do stuff
    } catch (IOException ex) {
        // handle ex           
    }
person Community    schedule 06.06.2015

Конечно, есть способ инкапсулировать это, он называется методом! Например, вы можете создать класс IOUtils следующим образом:

public class IOUtils {
    // this class is not meant to be instantiated
    private IOUtils() { }

    public static void closeQuietly(Closeable c) {      
       if (c == null) return;
       try {
          c.close();
       } catch (IOException e) { }
    }
}

А потом,

JarFile jar = ...;
URLClassLoader loader = ...;
try {
    // work ...
} finally {
    closeQuietly(jar);
    loader.close();
}

Как сказал Патрик Дж. Абае II, вы также можете использовать try-catch с ресурсами, но вы не всегда можете это делать, например, если вы сначала создаете InputStream в try-catch, а затем создайте несколько различных типов InputStream путем инкапсуляции первого (например, инкапсулируйте в CipherInputStream для расшифровки данных, а затем запишите в FileOutputStream). Тем не менее, я не говорю, что конструкция try-catch с ресурсами бесполезна, в большинстве случаев ее достаточно.

person Dici    schedule 06.06.2015
comment
Как вы предлагаете использовать этот класс? - person ; 06.06.2015
comment
@LutzHorn Что ты имеешь в виду? Он предназначен для использования как любой служебный класс с помощью статических методов. - person Dici; 06.06.2015
comment
Что ж, теперь, когда вы отредактировали свой ответ, становится яснее. - person ; 06.06.2015