По-рано се запознахме с IO API (Input/Output Application Programming Interface) и пакета java.io , чиито класове са основно за работа с потоци в Java. Ключът тук е концепцията за поток .
Днес ще започнем да разглеждаме NIO API (нов вход/изход).
Основната разлика между двата подхода към I/O е, че IO API е ориентиран към поток, докато NIO API е ориентиран към буфер. Така че основните понятия за разбиране са буфери и канали .
Какво е буфер и Howво е канал?
Каналът е логически портал, през който данните влизат и излизат, докато буферът е източникът or дестинацията на тези предадени данни . По време на изхода данните, които искате да изпратите, се поставят в буфер и буферът предава данните към канала. По време на въвеждане данните от канала се поставят в буфера.
С други думи:
- буферът е просто блок от паметта , в който можем да записваме информация и от който можем да четем информация,
- каналът е шлюз, който осигурява достъп до I/O устройства като файлове or сокети .
Каналите са много подобни на потоците в пакета java.io. Всички данни, които отиват навсякъде (or идват отвсякъде), трябва да преминат през обект на канал. Като цяло, за да използвате системата NIO, вие получавате канал към I/O обект и буфер за съхраняване на данни. След това работите с буфера, като въвеждате or извеждате данни според нуждите.
Можете да се движите напред и назад в буфер, т.е. да "разхождате" буфера, което е нещо, което не можете да правите в потоци. Това дава повече гъвкавост при обработката на данни. В стандартната библиотека буферите са представени от абстрактния клас Buffer и няколко от неговите наследници:
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- FloatBuffer
- DoubleBuffer
- LongBuffer
Основната разлика между подкласовете е типът данни, който съхраняват - byteове , ints , дълги и други примитивни типове данни.
Буферни свойства
Буферът има четири основни свойства. Това са капацитет, лимит, позиция и марка.
Капацитетът е максималното количество данни/byteове, които могат да се съхраняват в буфера. Капацитетът на буфера не може да се променя . След като буферът е пълен, той трябва да бъде изчистен, преди да пишете повече в него.
В режим на запис лимитът на буфера е същият като капацитета му, което показва максималното количество данни, които могат да бъдат записани в буфера. В режим на четене ограничението на буфера се отнася до максималното количество данни, които могат да бъдат прочетени от буфера.
Позицията показва текущата позиция на курсора в буфера. Първоначално е зададено на 0, когато буферът е създаден. С други думи, това е индексът на следващия елемент, който трябва да бъде прочетен or написан.
Знакът се използва за запазване на позиция на курсора . Докато манипулираме буфер, позицията на курсора се променя постоянно, но винаги можем да го върнем на предварително маркираната позиция.
Методи за работа с буфер
Сега нека да разгледаме основния набор от методи, които ни позволяват да работим с нашия буфер (блок памет) за четене и запис на данни към и от канали.
-
allocate(int capacity) — този метод се използва за разпределяне на нов буфер с посочения капацитет. Методът allocate() хвърля IllegalArgumentException , ако предаденият капацитет е отрицателно цяло число.
-
капацитет() връща капацитета на текущия буфер .
-
position() връща текущата позиция на курсора. Операциите за четене и запис преместват курсора в края на буфера. Връщаната стойност винаги е по-малка or равна на ограничението.
-
limit() връща ограничението на текущия буфер.
-
mark() се използва за маркиране (запазване) на текущата позиция на курсора.
-
reset() връща курсора на предварително маркираната (запаметена) позиция.
-
clear() задава позицията на нула и задава лимита на капацитета. Този метод не изчиства данните в буфера. Той само реинициализира позицията, границата и маркировката.
-
flip() превключва буфера от режим на запис в режим на четене. Той също така задава лимита на текущата позиция и след това я връща на нула.
-
read() — Методът за четене на канала се използва за запис на данни от канала в буфера, докато методът put() на буфера се използва за запис на данни в буфера.
-
write() — Методът за запис на канала се използва за запис на данни от буфера в канала, докато методът get() на буфера се използва за четене на данни от буфера.
-
rewind() пренавива буфера. Този метод се използва, когато трябва да препрочетете буфера - той задава позицията на нула и не променя лимита.
И сега няколко думи за каналите.
Най-важните реализации на канали в Java NIO са следните класове:
-
FileChannel — Канал за четене и запис на данни от/към файл.
-
DatagramChannel — Този клас чете и записва данни през мрежата чрез UDP (Протокол за потребителски дейтаграми).
-
SocketChannel — канал за четене и запис на данни през мрежата чрез TCP (протокол за контрол на предаването).
-
ServerSocketChannel — канал за четене и запис на данни през TCP връзки, точно Howто прави уеб сървър. За всяка входяща връзка се създаваSocketChannel
Практикувайте
Време е да напишем няколко реда code. Първо, нека прочетем file и да покажем съдържанието му на конзолата и след това да напишем няHowъв низ във file.
Кодът съдържа много коментари — надявам се те да ви помогнат да разберете How работи всичко:
// Create a RandomAccessFile object, passing in the file path
// and a string that says the file will be opened for reading and writing
try (RandomAccessFile randomAccessFile = new RandomAccessFile("text.txt", "rw");
// Get an instance of the FileChannel class
FileChannel channel = randomAccessFile.getChannel();
) {
// Our file is small, so we'll read it in one go
// Create a buffer of the required size based on the size of our channel
ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
// Read data will be put into a StringBuilder
StringBuilder builder = new StringBuilder();
// Write data from the channel to the buffer
channel.read(byteBuffer);
// Switch the buffer from write mode to read mode
byteBuffer.flip();
// In a loop, write data from the buffer to the StringBuilder
while (byteBuffer.hasRemaining()) {
builder.append((char) byteBuffer.get());
}
// Display the contents of the StringBuilder on the console
System.out.println(builder);
// Now let's continue our program and write data from a string to the file
// Create a string with arbitrary text
String someText = "Hello, Amigo!!!!!";
// Create a new buffer for writing,
// but let the channel remain the same, because we're going to the same file
// In other words, we can use one channel for both reading and writing to a file
// Create a buffer specifically for our string — convert the string into an array and get its length
ByteBuffer byteBuffer2 = ByteBuffer.allocate(someText.getBytes().length);
// Write our string to the buffer
byteBuffer2.put(someText.getBytes());
// Switch the buffer from write mode to read mode
// so that the channel can read from the buffer and write our string to the file
byteBuffer2.flip();
// The channel reads the information from the buffer and writes it to our file
channel.write(byteBuffer2);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Опитайте NIO API — ще ви хареса!
GO TO FULL VERSION