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

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

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


person Svish    schedule 25.05.2011    source источник


Ответы (5)


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

person WhiteFang34    schedule 25.05.2011
comment
Да, если бы я не знал, с каким Reader имею дело, я бы точно закрыл его. - person Svish; 25.05.2011

Он делает больше, чем это. Если я могу процитировать 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() для освобождения ресурсов.

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

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

person Stephan    schedule 25.05.2011
comment
Учитывая, что StringReader читает из строки, какие системные ресурсы она будет освобождать, если все, что она содержит, - это строка? - person mP.; 25.05.2011
comment
Как я уже сказал, для StringReader это не вопрос ресурсов. Но другие реализации Reader могут отличаться. В зависимости от вашего интерфейса и возможных будущих изменений, когда-нибудь он может стать актуальным, и даже если это всего лишь StringReader, это все еще хороший стиль. - person Stephan; 25.05.2011
comment
Я знаю, что документ говорит об этом, но при просмотре источника он только устанавливает для строки значение null (что, конечно, приведет к сбою других методов) - person Svish; 25.05.2011
comment
Может моя правка поможет? Похоже, вы только что объявили свою переменную с неправильным типом. StringReader.close() не объявляет исключение в моей копии Java6u20. - person Stephan; 25.05.2011

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

person mP.    schedule 25.05.2011

Вам не нужно перехватывать исключение, если ваша переменная имеет тип StringReader вместо Reader, поскольку StringReader#close() не генерирует исключение: только Reader#close() делает. Таким образом, вы можете использовать try-with-resources для автоматического закрытия читателю, без необходимости иметь шаблон для обработки исключений, которые не возникнут. Reader#close() выбрасывание 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.

Здесь работает обычная попытка с ресурсами, потому что 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);
    }
}

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

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

person Nils von Barth    schedule 30.08.2015

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

person prasad.gai    schedule 25.05.2011