Не приведет ли закрытие строки к утечке?

Я понимаю, что в java GC в конечном итоге будет очищать объекты, но я спрашиваю, не является ли плохой практикой не закрывать ваш обработчик строк, в настоящее время я делаю это:

 private static String processTemplate(final Template template, final Map root) {
        StringWriter writer = new StringWriter();
        try {
            template.process(root, writer);
        } catch (TemplateException e) {
            logger.error(e.getMessage());
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
        finally {

        }

        return writer.toString();
    }

Должен ли я закрыть писателя и создать новую строку следующим образом:

String result = "";

...

finally {
  result = writer.toString();
  writer.close();
}

Это лучше сделать?

4 ответа

Решение

Javadoc довольно явно:

Закрытие StringWriter не имеет никакого эффекта.

И быстрый взгляд на код подтверждает это:

public void close() throws IOException {
}

Он не содержит никаких ресурсов без памяти. Это будет мусор, как и все остальное. Вероятность закрытия close() существует просто потому, что другие объекты записи действительно содержат ресурсы, которые необходимо очистить, а метод close() необходим для защиты интерфейса.

Нет, не закрывая StringWriter не приведет к утечке: как отмечено, StringWriter#close() это nop, и писатель хранит только память, а не внешние ресурсы, поэтому они будут собраны, когда писатель будет собран. (Явно, он содержит ссылки на объекты в закрытых полях, которые не выходят за пределы объекта, конкретно StringBuffer, так что никаких внешних ссылок.)

Кроме того, вы обычно не должны закрывать StringWriterпотому что он добавляет шаблон к вашему коду, скрывая основную логику, как мы увидим. Однако, чтобы заверить читателей, что вы осторожны и делаете это намеренно, я бы рекомендовал прокомментировать этот факт:

// Don't need to close StringWriter, since no external resource.
Writer writer = new StringWriter();
// Do something with writer.

Если вы хотите закрыть средство записи, наиболее элегантным является использование try-with-resources, которое автоматически вызовет close() при выходе из тела блока try:

try (Writer writer = new StringWriter()) {
    // Do something with writer.
    return writer.toString();
}

Однако, поскольку Writer#close() выбрасывает IOExceptionВаш метод теперь должен также бросить IOException даже если это никогда не происходит, или вам нужно его перехватить, чтобы доказать компилятору, что он обработан. Это довольно сложно:

Writer writer = new StringWriter();
try {
    // Do something with writer, which may or may not throw IOException.
    return writer.toString();
} finally {
    try {
        writer.close();
    } catch (IOException e) {
        throw new AssertionError("StringWriter#close() should not throw IOException", e);
    }
}

Этот уровень шаблонного уровня необходим, потому что вы не можете просто поставить блокировку на весь блок try, иначе вы можете случайно проглотить IOException брошенный телом вашего кода. Даже если в настоящее время их нет, некоторые из них могут быть добавлены в будущем, и вы захотите, чтобы компилятор предупредил об этом. AssertionError документирует текущее поведение StringWriter#close(), который потенциально может измениться в будущем выпуске, хотя это крайне маловероятно; он также маскирует любое исключение, которое может возникнуть в теле попытки (опять же, это никогда не должно происходить на практике). Это слишком много шаблонного и сложного, и вам, безусловно, будет лучше, если вы пропустите close() и комментируя почему.

Тонкий момент заключается в том, что не только делает Writer#close() бросить IOExceptionно так же StringWriter#close(), поэтому вы не можете устранить исключение, сделав переменнуюStringWriterвместоWriter, Это отличается от String Reader, который переопределяетclose()метод и указывает, что онне выдает исключение! См. Мой ответ на вопрос " Должен ли я закрыть StringReader?, Это может выглядеть неправильно - зачем вам метод, который ничего не делает, но может вызвать исключение? - но предположительно для прямой совместимости, чтобы оставить открытой возможность броска IOException на закрытие в будущем, так как это проблема для писателей в целом. (Это также может быть просто ошибкой.)

Подводя итог: это нормально, чтобы не закрыть StringWriterНо причина, по которой мы не делаем обычные правильные вещи, а именно: попробуй с ресурсами, заключается просто в том, что close() заявляет, что он выдает исключение, которое он на самом деле не выдает на практике, и обработка этого точно - много шаблонного. В любом другом случае лучше просто использовать общепринятый правильный шаблон управления ресурсами и предотвратить проблемы и царапины на голове.

В конце метода нет ссылки на writer поэтому он будет освобожден GC.

Другие вопросы по тегам