Channels и Buffers
Другая модель движения данных
Классические потоки обычно воспринимаются как последовательность данных, которую мы читаем или записываем. В NIO часто используется другая связка: channel соединяет программу с источником или приёмником, а buffer хранит блок данных, который передаётся между ними.
Например, FileChannel работает с файлом,
а ByteBuffer хранит байты для очередной операции чтения или записи.
try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = channel.read(buffer);
}
Вызов read(buffer) записывает полученные из канала байты внутрь буфера.
Сам буфер при этом хранит не только массив данных, но и состояние, которое показывает,
где сейчас находится следующая операция.
position, limit и capacity
capacity— общий размер буфера;position— позиция следующего чтения или записи;limit— граница, дальше которой текущая операция идти не должна.
После того как канал записал данные в буфер, position стоит после последнего записанного байта.
Чтобы затем читать эти данные из буфера, обычно вызывают flip():
он устанавливает предел на текущую позицию и переводит позицию в начало.
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.put((byte) 10);
buffer.put((byte) 20);
buffer.flip();
System.out.println(buffer.get()); // 10
Канал умеет больше, чем однонаправленный поток
Некоторые каналы поддерживают чтение и запись через один объект.
FileChannel также умеет работать с текущей позицией в файле,
читать или писать по указанной позиции и передавать блоки данных между каналами.
Это полезно, когда требуется больше контроля, чем дают простые последовательные потоки.
FileChannel и работа с ByteBuffer полезны и в обычном блокирующем файловом IO.
Неблокирующие каналы — отдельная возможность NIO, а не свойство каждого канала.