Закрытие Java InputStreams

У меня есть некоторые вопросы об использовании метода close() при использовании Java InputStreams. Из того, что я вижу и читаю от большинства разработчиков, вы всегда должны явно ссылаться на close() на InputStream, когда он больше не нужен. Но сегодня я искал использование файла свойств Java, и каждый пример, который я нашел, имеет что-то вроде этого:

Properties props = new Properties();
try {
    props.load(new FileInputStream("message.properties"));
    //omitted.
} catch (Exception ex) {}

В приведенном выше примере нет возможности явно вызвать функцию close(), потому что InputStream недоступен после его использования. Я видел много подобных применений InputStreams, хотя, похоже, это противоречит тому, что большинство людей говорит о явном закрытии. Я прочитал Oracle JavaDocs и не упоминает, закрывает ли метод Properties.load() InputStream. Мне интересно, является ли это вообще приемлемым или предпочтительнее сделать что-то большее, чем следующее:

Properties props = new Properties();
InputStream fis = new FileInputStream("message.properties");
try {
    props.load(fis);
    //omitted.
} catch (Exception ex) {
    //omitted.
} finally {
    try {
        fis.close();
    } catch (IOException ioex) {
        //omitted.
    }
}

Какой способ лучше и/или эффективнее? Или это действительно имеет значение?

Ответы

Ответ 1

Примеры в "Свойства" закрывают FileInputStream явно после загрузки, поэтому я считаю безопасным предположить, что метод load не несет ответственности за это, вы.

// create and load default properties
Properties defaultProps = new Properties();
FileInputStream in = new FileInputStream("defaultProperties");
defaultProps.load(in);
in.close();

Для справки я проверил Apache Harmony Properties, и он не закрывает поток при загрузке.

Ответ 2

Класс Properties переносит входной поток в LineReader для чтения файла свойств. Поскольку вы предоставляете входной поток, это ваша ответственность за его закрытие.

Второй пример - лучший способ обработки потока на сегодняшний день, не полагайтесь на кого-то другого, чтобы закрыть его для вас.

Одним из улучшений, которые вы можете сделать, является использование IOUtils.closeQuietly()

http://commons.apache.org/io/api-1.2/org/apache/commons/io/IOUtils.html#closeQuietly(java.io.InputStream)

чтобы закрыть поток, например:

Properties props = new Properties();
InputStream fis = new FileInputStream("message.properties");
try {
    props.load(fis);
    //omitted.
} catch (Exception ex) {
    //omitted.
} finally {
    IOUtils.closeQuietly(fis);
}

Ответ 3

Я бы пошел с try-with-ресурсами (по крайней мере, для Java 7 +):

Properties props = new Properties();

try(InputStream fis = new FileInputStream("message.properties")) {
    props.load(fis);
    //omitted.
} catch (Exception ex) {
    //omitted.
}

Вызов функции close() должен быть вызван автоматически при выходе из блока try.

Ответ 4

В документации не упоминается, что props.load закрывает входной поток. Вы должны закрыть входной поток вручную в блоке finally, как вы предлагаете.

Это не нормально для функции, чтобы закрыть InputStream. Это же соглашение применяется как к памяти на языках, не связанных с мусором: если возможно, тот, кто открывает поток, должен закрыть поток. В противном случае очень легко оставить поток открытым (вы думаете, что функция собирается закрыть его, но это не так или что-то...)

Ответ 5

Если вы используете Java 7+, вы можете использовать это:

try(InputStream is = new FileInputStream("message.properties")) {
    // ...
}

Ответ 6

Похоже, что первый пример кода заканчивается использованием метода finalize в FileInputStream для фактического закрытия файла. Я бы сказал, что ваш второй пример лучше, хотя в обоих случаях файл закрывается.

Есть такие случаи, как потоки байтов, где close ничего не делает и может быть опущен, иначе я думаю, что лучше явно закрыть файл в блоке finally. Если вы откроете его, закройте его.

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

Вы почти всегда лучше выполняете свою собственную очистку вместо того, чтобы полагаться на финализатор. Использование финализатора также может оставить за собой критические ресурсы, которые не будут восстановлены в течение неопределенного периода времени. Если вы планируете использовать финализатор для обеспечения своевременного освобождения важных ресурсов, вам может потребоваться пересмотреть.

Ответ 7

Позвольте мне добавить кое-что к другим людям.

Если вы можете импортировать Apache Commons IO, вы можете использовать всегда удобную AutoCloseInputStream: вы завершаете свой InputStream, а затем просто используете свой завернутый экземпляр, и он автоматически закрывается, как только когда конец ввода достигнут или когда поток явно закрыт, в зависимости от того, что наступит раньше.