Должен ли я закрыть StringReader?
Я использую StringReader
чтобы превратить строку во что-то, что я могу загрузить на сервер SFTP (он принимает поток). Есть ли смысл закрывать StringReader
после этого? Насколько я могу видеть в источнике, он просто устанавливает строку в null
...
Я мог бы просто сделать это, но так как метод close помечен как бросающий IOException
и все, что мне нужно, чтобы обернуть это в попытку, и код просто выглядит намного ужаснее, чем, возможно, должно быть.
5 ответов
Если вы знаете, что имеете дело с StringReader
что ты будешь выбрасывать, я не вижу смысла закрывать это. Я не могу представить себе причину, по которой вы будете ссылаться на нее после того, как закроете ее, так что нет никакой реальной выгоды для строки, установленной в null
для сбора мусора. Если вы создавали метод, который принимает Reader
тогда может иметь смысл закрыть его, так как вы не знаете базовый тип.
Это делает больше, чем это. Если я могу процитировать 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 (в этом случае без обработки исключений).
Хотя это строго необязательно, потому что StringReader поддерживает только строку, так как в любом случае полезно всегда закрывать все читатели. Сегодня ваш код может использовать StringReader, но если вы замените его на другой Reader, который действительно должен быть закрыт, ваш код без закрытия будет неправильным, а ваш с закрытием будет в порядке.
Вам не нужно перехватывать исключение, если ваша переменная имеет тип 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
,
Помните, что это не относится к String Writer: 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
откуда-то еще (как, скажем, тип возврата фабрики), вам нужно закрыть его и обработать возможное исключение, так как вы не знаете, какие ресурсы оно может содержать, и оно может вызвать исключение.
Если вы закроете поток и освободите любые системные ресурсы, связанные с ним. Как только поток будет закрыт, дальнейшие вызовы read(), ready(), mark() или reset () вызовут IOException. Закрытие ранее закрытого потока не имеет никакого эффекта. Указано: закрыть в интерфейсе Closeable Указано: закрыть в классе Reader