Должен ли я закрывать StringReader?

Я использую StringReader, чтобы превратить строку в то, что я могу загрузить на SFTP-сервер (требуется поток). Есть ли какая-нибудь точка в закрытии этого StringReader после? Насколько я могу видеть в источнике, он просто устанавливает строку в null...

Я мог бы просто сделать это, но так как метод close отмечен как бросающий IOException, и все, что я должен обернуть его в try catch, и код просто заканчивается тем, что выглядит намного более ужасным, чем это может быть необходимо.

Ответ 1

Если вы знаете, что имеете дело с StringReader, который вы будете выбрасывать, я не вижу причин для его закрытия. Я не могу представить, почему вы будете ссылаться на него после того, как вы его закроете, поэтому нет реальной выгоды для строки, которая установлена ​​в null для сбора мусора. Если вы создавали метод, который принимает Reader, тогда имеет смысл закрыть его, поскольку вы не знаете базовый тип.

Ответ 2

Хотя строго не нужно, потому что StringReader держится только на String, в качестве хорошей формы всегда есть хорошая идея закрыть все Читатели. Сегодня ваш код может использовать StringReader, но если вы изменили его на другой Reader, который действительно нужно закрыть, ваш код будет закрыт, если ваш w/close будет прав.

Ответ 3

Он делает больше, чем это. Если я могу процитировать JavaDoc:

/**
 * Closes the stream and releases any system resources associated with
 * it. Once the stream has been closed, further read(),
 * ready(), mark(), or reset() invocations will throw an IOException.
 * Closing a previously closed stream has no effect.
 */

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

Итак, это хороший стиль, чтобы предотвратить дальнейшее (возможно, неправильное) использование этого экземпляра, как только вы закончите с ним. И поскольку это не повредит, это только предотвратит возможные ошибки в будущем.

Edit: Поскольку вы говорите, что ваш метод close() объявляет исключение, которое он может выбросить, я бы сказал, что вам нужно вызвать close(), поскольку StringReader.close() не генерирует исключение. Однако Reader.close(). Таким образом, вы уже разрешаете другие реализации Reader, поэтому вы должны закрыть его, так как вы не можете знать, какие реализации Reader вы в конечном итоге получите. Если мы говорим о трех строках кода, которые никогда не покидают эту область, объявите переменную StringReader и вызовите close в любом случае (в этом случае без обработки исключений).

Ответ 4

Вам не нужно ловить исключение, если ваша переменная имеет тип StringReader вместо Reader, так как StringReader#close() не генерирует исключение: только Reader#close(). Таким образом, вы можете использовать try-with-resources, чтобы автоматически закрыть читатель, без необходимости иметь шаблон для обработки исключений, которые не будут выполняться. Reader#close() throwing IOException означает, что подтипы могут вызывать этот тип исключения, а не то, что они должны. Это один из редких случаев, когда вы хотите объявить переменную с подтипом, а не супертипом; см. Использовать интерфейс или тип для определения переменной в java? для более.

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

try (StringReader reader = new StringReader(string)) {
    // Do something with reader.
}

Однако при закрытии StringReader мало значения, поскольку он не содержит внешний ресурс (скажем, используется только управляемая Java память, а не дескриптор файла или встроенная память), поэтому можно опустить ее, хотя Я бы рекомендовал комментарий, поясняющий, почему это безопасно, поскольку в противном случае не закрывать читателя удивительно. Как вы заметили, close() просто отключает поле, за источник JDK 8: StringReader.java:198. Если вы хотите избежать вложенности и закрытия, вы можете просто написать это:

// Don't need to close StringReader, since no external resource.
StringReader reader = new StringReader(string);
// Do something with reader.

... или (используя более общий тип для переменной):

// Don't need to close StringReader, since no external resource.
Reader reader = new StringReader(string);
// Do something with reader.

Здесь работает обычный try-with-resources, потому что StringReader#close() переопределяет Reader#close() и милостиво заявляет, что он не бросает IOException.

Остерегайтесь, что это не относится к StringWriter: StringWriter#close() заявляет, что он выбрасывает IOException, несмотря на то, что это nop! Вероятно, это связано с передовой совместимостью, поэтому оно может генерировать исключение в будущей реализации, хотя это маловероятно. См. мой ответ Не будет ли закрытие стриптизера причиной утечки?.

В таком случае (если метод не генерирует исключение, но интерфейс говорит, что он может), то жесткий способ записать это, о котором вы, по-видимому, намекаете, есть:

Reader reader = new StringReader(string);
try {
    // Do something with reader, which may or may not throw IOException.
} finally {
    try {
        reader.close();
    } catch (IOException e) {
        throw new AssertionError("StringReader#close() cannot throw IOException", e);
    }
}

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

Этот ответ зависит от того, что вы создаете StringReader самостоятельно; конечно, если вы получаете Reader откуда-то еще (в качестве типа возврата factory, скажем), тогда вам нужно закрыть его и обработать возможное исключение, поскольку вы не знаете, какие ресурсы он может удерживать, и это может вызвать исключение.

Ответ 5

Если вы закрываете поток и освобождаете связанные с ним системные ресурсы. Как только поток будет закрыт, вызовы read(), ready(), mark() или reset() будут вызывать исключение IOException. Закрытие ранее закрытого потока не влияет. Определяется: закрыть в интерфейсе Closeable Определяется: закрыть в классе Reader