InputStream.close: примеры (JAVA)
InputStream.close: voidОбщее описание и сигнатура
Метод InputStream.close() предназначен для завершения работы входного потока и освобождения связанных ресурсов (например, дескрипторов файлов, сетевых соединений, нативных буферов). В Java InputStream определён в java.io и реализует интерфейс Closeable, а значит метод close() имеет следующую сигнатуру:
public void close() throws java.io.IOException
Основные свойства и поведение:
- Аргументы: метод не принимает параметров.
- Возвращаемое значение:
void, возвращает ничего. - Исключения: может бросить
IOException, если при закрытии произошла ошибка ввода-вывода. - Идемпотентность: для большинства реализаций повторный вызов
close()безопасен и просто игнорируется; поведение зависит от конкретного подкласса. - Взаимодействие с обёртками: закрытие внешнего (обёрнутого) потока обычно приводит к закрытию вложенного потока. Некоторые классы (например,
FilterInputStream) закрывают делегируемый поток по умолчанию. - Связь с AutoCloseable: поскольку
CloseableрасширяетAutoCloseable, потоки можно использовать в try-with-resources, что обеспечивает автоматический вызовclose()и сохранение подавленных исключений. - Последствия закрытия: после закрытия операции чтения или получения дескриптора обычно приводят к IOException или к возврату -1 (в случае некоторых реализаций), поэтому поток становится недоступен.
Примечание: конкретные реализации (например, ByteArrayInputStream) могут делать close() «пустой» операцией и не выбрасывать исключений.
Простые варианты использования
Несколько коротких примеров с кодом и результатом.
1) Ручное закрытие в finally (стандартный приём до Java 7):
InputStream in = null;
try {
in = new FileInputStream("test.txt");
int b = in.read();
System.out.println(b);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {
}
}
}
(выведет первый байт файла или исключение при ошибке)
2) Try-with-resources (рекомендуемый способ):
try (InputStream in = new FileInputStream("test.txt")) {
byte[] buf = new byte[16];
int read = in.read(buf);
System.out.println(read);
} catch (IOException e) {
e.printStackTrace();
}
(выведет количество прочитанных байт или стек при ошибке)
3) Закрытие уже закрытого потока (на многих реализациях безопасно):
ByteArrayInputStream bais = new ByteArrayInputStream(new byte[] {1,2});
bais.close();
bais.close();
System.out.println("closed twice");
closed twice
4) Ошибка при закрытии приводит к IOException:
// Симуляция через кастомный InputStream
InputStream bad = new InputStream() {
@Override
public int read() throws IOException { return -1; }
@Override
public void close() throws IOException { throw new IOException("close failed"); }
};
try {
bad.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
close failed
Похожие API в Java
- OutputStream.close() - закрывает поток вывода; особенности: может потребоваться flush перед закрытием, закрытие также может выбросить IOException.
- Reader.close() / Writer.close() - аналоги для символьных потоков в java.io.
- java.nio.channels.Channel.close() - метод для каналов NIO; бросает IOException, часто используется вместе с
ByteBufferи каналами файлов. - Socket.close() - для сетевых сокетов; при закрытии разрывает соединение и может влиять на потоковую семантику (shutdown).
Когда выбирать: для побитовых/байтовых операций использовать InputStream/OutputStream; для символов предпочесть Reader/Writer; для более низкоуровневого доступа и неблокирующего ввода-вывода - NIO каналы.
Аналоги в других языках и отличия
Краткие соответствия и примерные отличия с небольшими примерами и результатами.
- PHP:
fclose($handle). Освобождает ресурс. Пример:
$f = fopen('php://memory', 'r');
fclose($f);
var_dump(is_resource($f));
bool(false) // в большинстве версий
- JavaScript (Node.js):
stream.destroy()или закрытие файла черезfs.close(). В потоках Web Streams -readable.cancel()илиwritable.close(). Пример (Node):
const fs = require('fs');
const fd = fs.openSync('test.txt', 'r');
fs.closeSync(fd);
console.log('closed');
closed
- Python:
file.close()и менеджер контекстаwith open(...) as f:. Пример:
with open('test.txt','rb') as f:
b = f.read(1)
print(b)
b'\x..' или b'' в зависимости от содержимого
- C#:
Stream.Close()иusing(вариант с Dispose). Пример:
using (var fs = new FileStream("test.txt", FileMode.Open)) {
int b = fs.ReadByte();
Console.WriteLine(b);
}
(первый байт или -1)
- Go: интерфейс
io.Closerиdefer f.Close(). Пример:
f, _ := os.Open("test.txt")
defer f.Close()
buf := make([]byte, 1)
f.Read(buf)
fmt.Println(buf[0])
(число - значение первого байта)
- Kotlin: использует те же java.io API и try-with-resources через use:
FileInputStream(path).use { ... }. - Lua:
file:close()для файлов, аналогично освобождает ресурс.
Отличия: в большинстве языков закрытие ресурса освобождает нативные дескрипторы, но семантика ошибок и авто-управления ресурсами различается: Java предоставляет try-with-resources и механизм подавления исключений; Python и C# имеют аналоги в виде менеджеров контекста и using.
Типичные ошибки и ситуации
- Забыть закрыть поток - утечка дескрипторов и возможный исчерпанный пул файловых дескрипторов. Пример:
for (int i=0;i<100000;i++) {
new FileInputStream("test.txt"); // ресурс не закрывается
}
System.out.println("done");
Возможная ошибка: Too many open files (в зависимости от ОС)
- Подавление исключения при закрытии без логирования - потеря информации об ошибках. Неправильно:
try { in.close(); } catch (IOException ignore) { }
(информация об ошибке теряется)
- Закрытие System.in - влияние на всю программу, так как поток глобален. Пример:
System.in.close();
int x = System.in.read(); // бросит IOException
java.io.IOException: Stream closed
- Ожидание, что close автоматически выполнит flush аналогично OutputStream - у InputStream flush не применимо, но при комбинировании с обёртками поведение может быть неожиданным.
- Закрытие обёрнутого потока, когда это не нужно. Решение - использовать обёртку, которая переопределяет close как no-op:
FilterInputStream noClose = new FilterInputStream(in) {
@Override
public void close() throws IOException { /* no-op */ }
};
noClose.close(); // не закроет in
(вложенный in остаётся открытым)
Эволюция механизма закрытия потоков
- Java 7: добавлен try-with-resources и механизм подавления исключений (suppressed exceptions), что упростило корректное закрытие ресурсoв и учёт исключений из close().
- Java 9: улучшение try-with-resources - переменные, эффективно финальные, можно использовать без повторного объявления в заголовке try.
- Семантика
close()в базовом API не менялась: метод остаётся void throws IOException, конкретные реализации могли получить дополнительную обработку в новых версиях, но обратная совместимость сохраняется.
Расширенные и редкие сценарии использования
Несколько подробных примеров с пояснениями.
1) Try-with-resources с несколькими ресурсами и подавленными исключениями:
try (InputStream in = new FileInputStream("a.txt");
InputStream in2 = new FileInputStream("b.txt")) {
// работа с потоками
throw new RuntimeException("work failed");
} catch (Exception e) {
e.printStackTrace();
}
// В стеке будут видны RuntimeException и, при ошибках в close(), исключения как suppressed
Пояснение: если при закрытии одного из потоков возникнет IOException, оно добавится как suppressed к основному исключению, а не потеряется.
2) Предотвращение закрытия вложенного потока (обёртка no-close):
class NoCloseInputStream extends FilterInputStream {
protected NoCloseInputStream(InputStream in) { super(in); }
@Override
public void close() throws IOException { /* ignore */ }
}
FileInputStream fis = new FileInputStream("test.txt");
InputStream wrapper = new NoCloseInputStream(fis);
wrapper.close();
System.out.println(fis.read()); // поток fis всё ещё открыт
(значение первого байта или -1)
Пояснение: полезно, когда внешний код должен закрыть свой декоратор, но основной ресурс должен оставаться открытым.
3) Ресурсное ограничение и таймаут при закрытии (через отдельный поток):
// Пример иллюстративный: при зависании close() можно прервать поток
InputStream slow = new InputStream() {
@Override
public int read() throws IOException { return -1; }
@Override
public void close() throws IOException {
try { Thread.sleep(10000); } catch (InterruptedException e) { throw new IOException("interrupted"); }
}
};
Thread t = new Thread(() -> {
try { slow.close(); } catch (IOException e) { System.out.println(e.getMessage()); }
});
t.start();
Thread.sleep(100); // даёт начало
t.interrupt();
interrupted
Пояснение: закрытие, выполняющее тяжёлую блокирующую работу, может блокировать поток выполнения. Управление прерыванием помогает избежать зависаний при завершении приложения.
4) Пользовательская реализация InputStream с освобождением нативных ресурсов:
class NativeBackedInputStream extends InputStream {
private long nativeHandle; // nativе ресурс
public NativeBackedInputStream(long handle) { this.nativeHandle = handle; }
@Override
public int read() throws IOException { return -1; }
@Override
public void close() throws IOException {
if (nativeHandle != 0) {
// вызов native-метода освобождения
// Native.free(nativeHandle);
nativeHandle = 0;
}
}
}
NativeBackedInputStream n = new NativeBackedInputStream(123L);
try { n.close(); } catch (IOException e) { }
(освобождение нативных ресурсов выполнено)
Пояснение: при интеграции с нативным кодом важно корректно освобождать нативные дескрипторы и учесть поведение при многократном close().
5) Взаимодействие PipedInputStream и PipedOutputStream:
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(out);
out.write(1);
out.close();
int r = in.read();
System.out.println(r);
in.close();
1
Пояснение: закрытие выходного потока сигнализирует о конце данных для входного потока, после чего read() вернёт -1.