Wcześniej spotkaliśmy się z IO API (Input & Output Application Programming Interface) oraz pakietem java.io , w którego klasach skoncentrowana jest główna funkcjonalność pracy ze strumieniami w Javie. Kluczem jest tu koncepcja strumienia .
Dzisiaj zaczniemy przyglądać się NIO API (New Input & Output).
Główna różnica między tymi dwoma podejściami we/wy polega na tym, że interfejs API IO jest zorientowany na strumień, podczas gdy interfejs API NIO jest zorientowany na bufor. Głównymi pojęciami w tym przypadku są pojęcia bufora ( bufor ) i kanału ( kanał ).
Co to jest bufor i kanał?
Kanał to logiczny portal, za pośrednictwem którego odbywa się wejście/wyjście danych, a bufor to źródło lub miejsce docelowe tych transmitowanych danych. Podczas wysyłania dane, które chcesz wysłać, są buforowane i przekazywane do potoku. Na wejściu dane z kanału są buforowane.
Innymi słowy:
- bufor to po prostu blok pamięci, w którym możemy zapisywać informacje iz którego możemy odczytywać informacje,
- kanał to brama, która umożliwia dostęp do urządzeń we/wy, takich jak plik lub gniazdo.
Kanały są bardzo podobne do strumieni w pakiecie java.io. Wszystkie dane, które trafiają gdziekolwiek (lub pochodzą z dowolnego miejsca) muszą przechodzić przez obiekt kanału. Ogólnie rzecz biorąc, aby korzystać z systemu NIO, otrzymujesz kanał do urządzenia wejścia/wyjścia oraz bufor do przechowywania danych. Następnie pracujesz z buforem, wprowadzając lub wyprowadzając dane w razie potrzeby.
Możesz poruszać się do przodu i do tyłu wzdłuż bufora, czyli „chodzić” po nim, czego nie mogłeś robić w strumieniach. Daje to większą elastyczność w przetwarzaniu danych. W bibliotece standardowej bufor jest reprezentowany przez abstrakcyjną klasę Buffer i jej wielu następców:
- ByteBuffer
- CharBuffer
- Krótki Bufor
- IntBuffer
- FloatBuffer
- Podwójny bufor
- Długi Bufor
Główną różnicą między spadkobiercami jest typ danych, który będą przechowywać - byte , int , long i inne prymitywne typy danych.
Właściwości bufora
Bufor ma cztery główne właściwości. Są to pojemność, limit, pozycja i znacznik.
Pojemność ( Pojemność ) - maksymalna ilość danych/bajtów, które mogą być przechowywane w buforze. Nie można zmienić pojemności bufora . Po zapełnieniu bufora należy go wyczyścić przed zapisem do niego.
Limit ( Limit ) - W trybie zapisu bufora Limit jest równy pojemności, która wskazuje maksymalną ilość danych, które można zapisać w buforze. W trybie odczytu bufora Limit odnosi się do maksymalnej ilości danych, które można odczytać z bufora.
Position ( Position ) - wskazuje aktualną pozycję kursora w buforze. Początkowo ustawiona na 0 podczas tworzenia bufora. Innymi słowy, jest to indeks elementu, który ma zostać odczytany lub zapisany.
Marker ( Mark ) - służy do zaznaczania aktualnej pozycji kursora. W procesie manipulowania buforem pozycja kursora ciągle się zmienia, ale zawsze możemy przywrócić go do wcześniej zaznaczonej pozycji.
Metody buforowe
Przyjrzyjmy się teraz głównemu zestawowi metod, które pozwalają nam pracować z naszym buforem (blokiem pamięci) do odczytu i zapisu danych do iz kanałów.
-
allocate(int pojemność) — Metoda służy do przydzielenia nowego bufora z pojemnością jako parametrem. Metoda allocate() zgłasza wyjątek IllegalArgumentException , jeśli przekazana pojemność jest ujemną liczbą całkowitą.
-
Capacity() - zwraca pojemność ( pojemność ) bieżącego bufora.
-
position() - zwraca aktualną pozycję kursora. Obie operacje odczytu i zapisu przesuwają kursor na koniec bufora. Wartość zwracana jest zawsze mniejsza lub równa limitowi.
-
limit() - zwraca limit bieżącego bufora.
-
mark() - służy do zaznaczania (zaznaczania) aktualnej pozycji kursora.
-
reset() - przywróci kursor do poprzednio zaznaczonej (zaznaczonej) pozycji.
-
clear() - ustawia pozycję na zero i ogranicza ją do pojemności. W tej metodzie dane w buforze nie są czyszczone , tylko ponownie inicjowane są znaczniki.
-
flip() - Przełącza tryb buforowania z trybu zapisu na tryb odczytu. Ustawia również pozycję z powrotem na zero i ustala limit, w którym pozycja znajdowała się w momencie pisania.
-
read() to metoda odczytu kanału używana do zapisywania danych z kanału do bufora, a put() to metoda bufora, która służy do zapisywania danych do bufora.
-
write() - Metoda write kanału służy do zapisu danych z bufora do kanału, natomiast get() to metoda buffer, która służy do odczytu danych z bufora.
-
rewind() - metoda przewijania do tyłu. Używany, gdy wymagany jest ponowny odczyt, ponieważ ustawia pozycję na zero i nie zmienia wartości granicznej.
A teraz - trochę o kanale.
Najważniejszymi implementacjami kanałów w Javie NIO są następujące klasy:
-
FileChannel to kanał do odczytu i zapisu danych do pliku.
-
DatagramChannel - odczytuje i zapisuje dane przez sieć poprzez UDP (User Datagram Protocol).
-
SocketChannel to kanał do odczytu i zapisu danych przez sieć za pośrednictwem protokołu TCP (Transmission Control Protocol).
-
ServerSocketChannel to kanał do odczytu i zapisu danych przez połączenia TCP, tak jak robi to serwer WWW. Dla każdego połączenia przychodzącego tworzony jest kanał SocketChannel .
Ćwiczyć
Czas napisać kilka linijek kodu. Najpierw wczytajmy plik i wyświetlmy jego zawartość w konsoli, a następnie wpiszmy do pliku dowolną linię, którą stworzyliśmy.
Kod zawiera dużo komentarzy, mam nadzieję, że pomogą Ci zrozumieć jak to wszystko działa:
// 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();
}
Wypróbuj NIO API , a pokochasz to!
GO TO FULL VERSION