попробуйте-с-ресурсами, где обернуть поток с помощью InputStreamReader?

возможно, я слишком много думаю, но я только что написал код:

try (InputStream in = ModelCodeGenerator.class.getClassLoader().getResourceAsStream("/model.java.txt"))
{
    modelTemplate = new SimpleTemplate(CharStreams.toString(new InputStreamReader(in, "ascii")));
}

что означает, что InputStreamReader никогда не закрывается (но в этом случае мы знаем, что его метод close просто закрывает базовый InputStream.)

можно написать так:

try (InputStreamReader reader = new InputStreamReader(...))

но это кажется еще хуже. Если InputStreamReader бросает по какой-то причине, InputStream никогда не будет закрыт, верно? Это общая проблема в C++ с конструкторами, которые вызывают другие конструкторы. Исключения может вызвать утечку памяти / ресурсов.

есть ли лучшая практика здесь?

2 ответов


что означает, что InputStreamReader никогда не закрывается

Ась? В твоем коде так и есть... И он, безусловно, справится .также закройте () вашего потока ресурсов. Подробнее см. ниже...

как @ SotiriosDelimanolis упоминает однако вы можете объявить несколько ресурсов в "блоке ресурсов" инструкции try-with-resources.

у вас есть еще одна проблема здесь:.getResourceAsStream() может возвращать null; вы можете поэтому имейте NPE.

на вашем месте я бы сделал это:

final URL url = ModelCodeGenerator.class.getClassLoader()
    .getResource("/model.java.txt");

if (url == null)
    throw new IOException("resource not found");

try (
    final InputStream in = url.openStream();
    final Reader reader = new InputStreamReader(in, someCharsetOrDecoder);
) {
    // manipulate resources
}

есть очень важный момент, чтобы рассмотреть...

Closeable не распространяется AutoCloseable, Да; на самом деле он только отличается, "подпись мудрая", исключением (IOException vs Exception). Но есть фундаментальное различие в поведение.

из javadoc AutoCloseable ' s .close() (выделено мной):

обратите внимание, что в отличие от метода close Closeable этот метод close не обязательно должен быть идемпотентным. Другими словами, вызов этого метода close более одного раза может иметь видимый побочный эффект, в отличие от Closeable.закрыть, который не должен иметь никакого эффекта, если вызывается более одного раза. Однако разработчикам этого интерфейса настоятельно рекомендуется сделать свои методы close идемпотентными.

и действительно, javadoc Closeable понятно это:

закрывает этот поток и освобождает все связанные с ним системные ресурсы. Если поток уже закрыт, вызов этого метода не имеет никакого эффекта.

у вас есть два очень важных момента:

  • по контракту, a Closeable также заботится обо всех ресурсах, связанных с ним; поэтому, если вы закроете BufferedReader, который обертывает Reader, который оборачивается InputStream, все три закрыты;
  • если вы называете .close() более чем один раз, нет никаких побочных эффектов.

это также означает, конечно, что вы можете выбрать вариант paranoid и сохранить ссылку на все Closeable ресурсы и закройте их все; остерегайтесь, однако, если у вас есть AutoCloseable ресурсы в микс, которые не являются Closeable!


но это кажется еще хуже. Если InputStreamReader бросает по какой-то причине InputStream никогда не будет закрыт, верно?

это верно (хотя вряд ли,InputStreamReader конструктор не очень много делает).

на try-with-resources можно объявить столько ресурсов, сколько вы хотите. Объявите один для обернутого ресурса, а другой для InputStreamReader.

try (InputStream in = ModelCodeGenerator.class
             .getClassLoader()
             .getResourceAsStream("/model.java.txt");
    InputStreamReader reader = new InputStreamReader(in)) {...}

отметим, что getResourceAsStream потенциально может вернуться null, который причина InputStreamReader конструктор кинуть NullPointerException. Если вы хотите справиться с этим по-другому, адаптируйте, как вы получаете ресурс, который должен быть обернут.

учебник, связанный выше, представляет этот пример

try (
    java.util.zip.ZipFile zf =
         new java.util.zip.ZipFile(zipFileName);
    java.io.BufferedWriter writer = 
        java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {

с объяснением

в этом примере оператор try-with-resources содержит два объявления, разделенные точкой с запятой:ZipFile и BufferedWriter. Когда блок кода, который непосредственно следует за ним завершает, обычно или из-за исключения, закрытие методы BufferedWriter и ZipFile объекты автоматически называют в таком порядке. Обратите внимание, что методы close ресурсов позвонил в обратном порядке их создания.