Философия Java3
Шрифт:
Напрямую взаимодействует с каналом только буфер Byte Buffer, то есть буфер, хранящий простые байты. Если вы просмотрите документацию JDK для класса java.nio.ByteBuffer, то увидите что он достаточно прост: вы создаете его, указывая, сколько места надо выделить под данные. Класс содержит набор методов для получения и помещения данных в виде последовательности байтов или в виде примитивов. Однако возможности записать в него объект или даже простую строку нет. Буфер работает на достаточно низком уровне, поскольку обеспечивается более эффективная совместимость с большинством операционных систем.
Три
Простой пример использования всех трех типов потоков. Создаваемые каналы поддерживают запись, чтение/запись и только чтение:
//: io/GetChannel java // Получение каналов из потоков import java.nio.*, import java.nio channels *, import java io *;
public class GetChannel {
private static final int BSIZE = 1024; public static void main(String[] args) throws Exception { // Запись файла FileChannel fc =
new FileOutputStreamCdata.txt") getChannelО; fc write(ByteBuffer.wrap("Some text ".getBytesO)); fc. closeO,
// Добавление в конец файла fc =
new RandomAccessFileC'data.txt", "rw") getChannelО; fc position(fc.sizeO); // Переходим в конец fc write(ByteBuffer.wrap("Some more" getBytesO)), fc closeO; // Чтение файла;
fc = new FilelnputStreamC'data txt").getChannelО; ByteBuffer buff = ByteBuffer.allocate(BSIZE); fc.read(buff), buff .flipO;
while(buff .hasRemainingO)
System.out.print((char)buff getO);
}
} /* Output; Some text Some more *///:-
Для любого из рассмотренных выше классов потоков метод getChannel выдает канал FileChannel. Канал довольно прост: ему передается байтовый буфер ByteBuffer для чтения и записи, и вы можете заблокировать некоторые участки файла для монопольного доступа (этот процесс будет описан чуть позже).
Для помещения байтов в буфер ByteBuffer используется один из нескольких методов для записи данных (put); данные записываются в виде одного или нескольких байтов или значений примитивов. Впрочем, как было показано в примере, можно «заворачивать» уже существующий байтовый массив в буфер ByteBuffer, используя метод wrap. Когда вы так делаете, байтовый массив не копируется, а используется как хранилище для полученного буфера ByteBuffer. В таких случаях говорят, что буфер ByteBuffer создается на базе массива.
Файл data.txt заново открывается с помощью класса RandomAccessFile. Заметьте, что канал FileChannel может перемещаться внутри файла; в нашем примере он сдвигается в конец файла так, чтобы дополнительные записи присоединялись за существующим содержимым.
Чтобы доступ к файлу ограничивался только чтением, следует явно получить байтовый буфер ByteBuffer статическим методом allocate. Предназначение nio — быстрое перемещение большого количества данных, поэтому размер
Можно получить еще большее быстродействие, используя вместо метода allocate метод allocateDirect. Он производит буфер «прямого доступа», еще теснее привязанный к низкоуровневой работе операционной системы. Однако такой буфер требует больше ресурсов, а реализация его различается в различных операционных системах. Опять же, поэкспериментируйте со своим приложением и выясните, дадут ли буферы прямого доступа лучшую производительность.
После вызова метода read буфера FileChannel для сохранения байтов в буфере ByteBuffer также необходимо вызвать для буфера метод flip, позволяющий впоследствии извлечь из буфера его данные (да, все это выглядит немного неудобно, но помните, что расчет делался на высокое быстродействие, поэтому все делается на низком уровне). И если затем нам снова понадобится буфер для чтения, придется вызывать перед каждым методом read метод clear. В этом нетрудно убедиться на примере простой программы копирования файлов:
//• io/ChannelCopy java
// Копирование файла с использованием каналов и буферов
// {Параметры Channel Copy java test txt}
import java nio *.
import java nio.channels *.
import java io *,
public class ChannelCopy {
private static final int BSIZE = 1024, public static void main(String[] args) throws Exception { if(args length != 2) {
System out println("параметры Источник Приемник"), System exit(l);
}
FileChannel
in = new FileInputStream(args[0]).getChannelО. out = new FileOutputStream(args[l]).getChannelО, ByteBuffer buffer = ByteBuffer.allocate(BSIZE); while(in read(buffer) != -1) {
buffer flipO; // Подготовка к записи out.write(buffer):
buffer.clear; // Подготовка к чтению
}
}
} /// ~
В программе создаются два канала FileChannel: для чтения и для записи. Выделяется буфер ByteBuffer, а когда метод FileChannel.read возвращает -1, это значит, что мы достигли конца входных данных (без сомнения, пережиток UNIX и С). После каждого вызова метода read, помещающего данные в буфер, метод flip подготавливает буфер так, чтобы информация из него могла быть извлечена методом write. После вызова write информация все еще хранится в буфере, поэтому метод clear перемещает все его внутренние указатели, чтобы буфер снова был способен принимать данные в методе read.
Впрочем, рассмотренная программа не лучшим образом выполняет копирование файлов. Специальные методы, transferTo и transferFrom, позволяют напрямую присоединить один канал к другому:
// io/TransferTo java
// Использование метода transferToO для соединения каналов // {Параметры TransferTo java TransferTo txt} import java nio channels.*, import java io *.
public class TransferTo {
public static void main(String[] args) throws Exception { if(args length != 2) {
System out printin("параметры источник приемник"), System exit(l).