Нужно ли закрыть () и FileReader, и BufferedReader?

Я читаю локальный файл, используя BufferedReader, обернутый вокруг FileReader:

BufferedReader reader = new BufferedReader(new FileReader(fileName));
// read the file
// (error handling snipped)
reader.close();

Нужно ли мне close() FileReader также, или обертка справится с этим? Я видел код, где люди делают что-то вроде этого:

FileReader fReader = new FileReader(fileName);
BufferedReader bReader = new BufferedReader(fReader);
// read the file
// (error handling snipped)
bReader.close();
fReader.close();

Этот метод вызывается из сервлета, и я хотел бы убедиться, что я не оставляю открытых дескрипторов.

10 ответов

Решение

Нет.

BufferedReader.close()

закрывает поток в соответствии с javadoc для BufferedReader и InputStreamReader

так же как

FileReader.close()

делает.

Как уже отмечали другие, вам нужно только закрыть внешнюю обертку.

BufferedReader reader = new BufferedReader(new FileReader(fileName));

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

Интерфейс Closeable можно использовать, если конструктор-обертка в Java 5 или 6 может потерпеть неудачу:

Reader reader = new FileReader(fileName);
Closeable resource = reader;
try {
  BufferedReader buffered = new BufferedReader(reader);
  resource = buffered;
  // TODO: input
} finally {
  resource.close();
}

Код Java 7 должен использовать шаблон try-with-resources:

try (Reader reader = new FileReader(fileName);
    BufferedReader buffered = new BufferedReader(reader)) {
  // TODO: input
}

После проверки исходного кода я обнаружил, что для примера:

FileReader fReader = new FileReader(fileName);
BufferedReader bReader = new BufferedReader(fReader);

метод close() объекта BufferedReader будет вызывать абстрактный метод close() класса Reader, который в конечном итоге вызовет реализованный метод в классе InputStreamReader, который затем закроет объект InputStream.

Таким образом, достаточно только bReader.close().

Исходный код BufferedReader показывает, что базовый уровень закрывается при закрытии BufferedReader.

Согласно источнику BufferedReader, в этом случае bReader.close вызывает fReader.close, поэтому технически вам не нужно вызывать последний.

Начиная с Java 7 вы можете использовать оператор try-with-resources

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

Поскольку BufferedReader Экземпляр объявляется в операторе try-with-resource, он будет закрыт независимо от того, завершается ли оператор try нормально или внезапно. Так что вам не нужно закрывать его самостоятельно в finally заявление. (Это также относится и к вложенным операторам ресурсов)

Это рекомендуемый способ работы с ресурсами, более подробную информацию смотрите в документации.

Вам нужно только закрыть bufferedReader, т.е. reader.close(), и он будет работать нормально.

Вам нужно закрыть только BufferedReader в вашем сценарии.

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

Если у вас нет доступа к исходному коду и вы хотите узнать, вызывает ли ваш читатель и JVM различные читатели и потоки в вашей ситуации, вы можете переопределить close метод как простой тест.

      Path path = Paths.get("/home/example/test.txt");

InputStream fileInputStream = new FileInputStream(path.toFile()) {
    public void close() throws IOException {
        System.out.println("FileInputStream::close()");
        super.close();
    }
};
Reader inputStreamReader = new InputStreamReader(fileInputStream, Charsets.UTF_8) {
    public void close() throws IOException {
        System.out.println("InputStreamReader::close()");
        super.close();
    }
};
BufferedReader bufferedReader = new BufferedReader(inputStreamReader) {
    public void close() throws IOException {
        System.out.println("BufferedReader::close()");
        super.close();
    }
};

bufferedReader.close();

Когда вы запустите вышеуказанное, вы увидите что-то очень похожее на:

      BufferedReader::close()
InputStreamReader::close()
FileInputStream::close()

Поскольку в JavaDoc нет явной спецификации, мы не можем быть уверены в поведении всех JVM. Однако большинство читателей / потоков, похоже, следуют приведенному выше шаблону (например, вы можете добавить GZIPInputStream к приведенному выше примеру и увидите, что GZIPInputStream::close() также называется).

Я опоздал, но:

BufferReader.java:

public BufferedReader(Reader in) {
  this(in, defaultCharBufferSize);
}

(...)

public void close() throws IOException {
    synchronized (lock) {
        if (in == null)
            return;
        try {
            in.close();
        } finally {
            in = null;
            cb = null;
        }
    }
}

Вам не нужно закрывать завернутый читатель / писатель.

Если вы посмотрели на документы (Reader.close(), Writer.close()), Вы увидите это в Reader.close() это говорит:

Закрывает поток и освобождает любые системные ресурсы, связанные с ним.

Который просто говорит, что он "освобождает любые системные ресурсы, связанные с ним". Даже если это не подтверждает... это дает вам толчок, чтобы начать смотреть глубже. и если вы идете в Writer.close() это только заявляет, что это закрывает себя.

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

На BufferedWriter Line 265 вы увидите out.close(), Так что это не закрытие себя.. Это что-то еще. Если вы ищете в классе случаи " out "Вы заметите, что в конструкторе в строке 87, что out является автором, который переносит класс, где он вызывает другой конструктор, а затем назначает out параметр к своему собственному out переменная..

Так что.. как насчет других? Вы можете увидеть похожий код в строке BufferedReader 514, строке BufferedInputStream 468 и строке 199 InputStreamReader. Других я не знаю, но этого должно быть достаточно, чтобы предположить, что они знают.

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