Tidligere ble vi kjent med IO API (Input/Output Application Programming Interface) og java.io -pakken, hvis klasser hovedsakelig er for arbeid med strømmer i Java. Nøkkelen her er konseptet med en strøm .

I dag skal vi begynne å vurdere NIO API (New Input/Output).

Hovedforskjellen mellom de to tilnærmingene til I/O er at IO API er strømorientert mens NIO API er bufferorientert. Så de viktigste konseptene å forstå er buffere og kanaler .

Hva er en buffer og hva er en kanal?

En kanal er en logisk portal der data beveger seg inn og ut, mens en buffer er kilden eller destinasjonen for disse overførte dataene. Under utdata legges dataene du vil sende inn i en buffer, og bufferen sender dataene til kanalen. Under input blir dataene fra kanalen lagt inn i bufferen.

Med andre ord:

  • en buffer er ganske enkelt en minneblokk som vi kan skrive informasjon inn i og som vi kan lese informasjon fra,
  • en kanal er en gateway som gir tilgang til I/O-enheter som filer eller stikkontakter.

Kanaler ligner veldig på strømmer i java.io-pakken. Alle data som går hvor som helst (eller kommer fra hvor som helst) må passere gjennom et kanalobjekt. Generelt, for å bruke NIO-systemet får du en kanal til en I/O-enhet og en buffer for lagring av data. Deretter jobber du med bufferen, legger inn eller skriver ut data etter behov.

Du kan bevege deg fremover og bakover i en buffer, altså "gå" bufferen, som er noe du ikke kunne gjort i bekker. Dette gir mer fleksibilitet ved behandling av data. I standardbiblioteket er buffere representert av den abstrakte bufferklassen og flere av dens etterkommere:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • FloatBuffer
  • Dobbelbuffer
  • LongBuffer

Hovedforskjellen mellom underklassene er datatypen de lagrer - bytes , ints , longs og andre primitive datatyper.

Bufferegenskaper

En buffer har fire hovedegenskaper. Disse er kapasitet, grense, posisjon og merke.

Kapasitet er den maksimale mengden data/byte som kan lagres i bufferen. En buffers kapasitet kan ikke endres . Når en buffer er full, må den tømmes før du skriver mer til den.

I skrivemodus er en buffers grense den samme som dens kapasitet, og indikerer den maksimale mengden data som kan skrives til bufferen. I lesemodus refererer en buffers grense til den maksimale mengden data som kan leses fra bufferen.

Posisjonen indikerer gjeldende posisjon til markøren i bufferen. I utgangspunktet er den satt til 0 når bufferen opprettes. Det er med andre ord indeksen til det neste elementet som skal leses eller skrives.

Merket brukes til å lagre en markørposisjon. Når vi manipulerer en buffer, endres markørposisjonen hele tiden, men vi kan alltid returnere den til den tidligere markerte posisjonen.

Metoder for å arbeide med en buffer

La oss nå se på hovedsettet med metoder som lar oss jobbe med bufferen vår (minneblokk) for å lese og skrive data til og fra kanaler.

  1. allocate(int kapasitet) — denne metoden brukes til å tildele en ny buffer med spesifisert kapasitet. Allocate ()- metoden kaster et IllegalArgumentException hvis den beståtte kapasiteten er et negativt heltall.

  2. kapasitet() returnerer gjeldende buffers kapasitet .

  3. posisjon() returnerer gjeldende markørposisjon. Lese- og skriveoperasjoner flytter markøren til slutten av bufferen. Returverdien er alltid mindre enn eller lik grensen.

  4. limit() returnerer gjeldende buffers grense.

  5. mark() brukes til å merke (lagre) gjeldende markørposisjon.

  6. reset() returnerer markøren til den tidligere markerte (lagrede) posisjonen.

  7. clear() setter posisjonen til null og setter grensen for kapasiteten. Denne metoden sletter ikke dataene i bufferen. Den reinitialiserer bare posisjonen, grensen og merket.

  8. flip() bytter bufferen fra skrivemodus til lesemodus. Den setter også grensen til gjeldende posisjon og setter deretter posisjonen tilbake til null.

  9. read() — Kanalens lesemetode brukes til å skrive data fra kanalen til bufferen, mens bufferens put()- metode brukes til å skrive data til bufferen.

  10. write() — Kanalens skrivemetode brukes til å skrive data fra bufferen til kanalen, mens bufferens get()- metode brukes til å lese data fra bufferen.

  11. rewind() spoler bufferen tilbake. Denne metoden brukes når du trenger å lese bufferen på nytt - den setter posisjonen til null og endrer ikke grensen.

Og nå noen ord om kanaler.

De viktigste kanalimplementeringene i Java NIO er følgende klasser:

  1. FileChannel — En kanal for å lese og skrive data fra/til en fil.

  2. DatagramChannel — Denne klassen leser og skriver data over nettverket via UDP (User Datagram Protocol).

  3. SocketChannel — En kanal for lesing og skriving av data over nettverket via TCP (Transmission Control Protocol).

  4. ServerSocketChannel — En kanal for lesing og skriving av data over TCP-tilkoblinger, akkurat som en webserver gjør. En SocketChannel opprettes for hver innkommende tilkobling.

Øve på

Det er på tide å skrive et par linjer med kode. Først, la oss lese filen og vise innholdet på konsollen, og deretter skrive en streng til filen.

Koden inneholder mange kommentarer - jeg håper de vil hjelpe deg å forstå hvordan alt fungerer:


// 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();
}

Prøv NIO API - du vil elske det!