Потоки байт
InputStream и OutputStream — абстракции последовательного ввода/вывода байтов. Источник может быть файлом, сокетом, массивом, архивом.
Контракт чтения
int value = input.read();Метод возвращает 0..255 либо -1 при EOF. Поэтому нельзя сначала приводить результат к byte, а затем проверять -1: значение байта 0xff тоже станет -1.
Блочное копирование:
static long copy(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[8192];
long total = 0;
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
total += read;
}
return total;
}Один read(buffer) не обязан заполнить buffer. Использовать нужно только первые read байтов.
В современных JDK есть input.transferTo(output), но владение потоками и закрытие все равно определяет вызывающий код.
Ресурсы и буферы
try (InputStream input = Files.newInputStream(source);
OutputStream output = new BufferedOutputStream(
Files.newOutputStream(target))) {
input.transferTo(output);
}flush() проталкивает данные из Java-буферов к следующему слою, но не обещает физическую запись на носитель. close() обычно вызывает flush и освобождает ресурс. Не закрывать поток, которым владеет вызывающий код, без явного контракта.
Обертки
BufferedInputStream/OutputStreamуменьшают число обращений к нижнему слою;ByteArrayInputStream/OutputStreamработают в памяти;DataInputStream/OutputStreamчитают/пишут примитивы в согласованном бинарном формате;GZIPInputStream/OutputStreamдобавляют сжатие;InputStreamReaderдекодирует байты в символы — Потоки символов.
Для Data* порядок и тип чтения должны совпадать с записью. Такой ad-hoc формат нужно версионировать, если он хранится долго.
На собеседовании
- Почему
read()возвращает int? Чтобы представить все 256 байтов и отдельный EOF-1. - Гарантирует ли read(buffer) полный буфер? Нет.
- Что пишет
write(int)? Младшие 8 бит. - Что гарантирует flush? Передачу буферизованных данных нижнему слою, не fsync диска.
- Кто должен закрывать stream? Его владелец по контракту.