Korábban megismerhettük az IO API-t (Input/Output Application Programming Interface) és a java.io csomagot, amelyek osztályai elsősorban a Java nyelvű streamekkel való munkára szolgálnak. A kulcs itt a patak fogalma .

Ma elkezdjük megvizsgálni a NIO API-t (új bemenet/kimenet).

A fő különbség az I/O két megközelítése között az, hogy az IO API adatfolyam-orientált, míg a NIO API pufferorientált. Tehát a legfontosabb megértendő fogalmak a pufferek és a csatornák .

Mi az a puffer és mi a csatorna?

A csatorna egy logikai portál, amelyen keresztül az adatok be- és kifelé mozognak, míg a puffer az átvitt adatok forrása vagy célállomása. A kimenet során a küldeni kívánt adatok egy pufferbe kerülnek, és a puffer továbbítja az adatokat a csatornának. A bevitel során a csatorna adatai a pufferbe kerülnek.

Más szavakkal:

  • a puffer egyszerűen egy memóriablokk, amelybe információkat írhatunk, és amelyből információkat olvashatunk,
  • a csatorna egy átjáró, amely hozzáférést biztosít az I/O eszközökhöz, például fájlokhoz vagy socketekhez.

A csatornák nagyon hasonlítanak a java.io csomagban található adatfolyamokhoz. Minden adatnak, amely bárhová eljut (vagy bárhonnan érkezik), át kell haladnia egy csatorna objektumon. Általában a NIO rendszer használatához kap egy csatornát egy I/O entitáshoz és egy puffert az adatok tárolására. Ezután a pufferrel dolgozik, szükség szerint be- vagy kiadva adatokat.

A pufferben előre-hátra mozoghat, azaz "sétálhat" a pufferben, amit streamekben nem tudna megtenni. Ez nagyobb rugalmasságot biztosít az adatok feldolgozásakor. A szabványos könyvtárban a puffereket az absztrakt Buffer osztály és annak több leszármazottja képviseli :

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • FloatBuffer
  • DoubleBuffer
  • LongBuffer

A fő különbség az alosztályok között az általuk tárolt adattípus – bájtok , int , long és egyéb primitív adattípusok.

Puffer tulajdonságai

A puffernek négy fő tulajdonsága van. Ezek a kapacitás, határérték, pozíció és jel.

A kapacitás a pufferben tárolható adat/bájt maximális mennyisége. A puffer kapacitása nem módosítható . Ha egy puffer megtelt, törölni kell, mielőtt többet írna bele.

Írási módban a puffer korlátja megegyezik a kapacitásával, ami a pufferbe írható maximális adatmennyiséget jelzi. Olvasási módban a puffer korlátja a pufferből kiolvasható adatok maximális mennyiségére vonatkozik.

A pozíció a kurzor aktuális pozícióját jelzi a pufferben. A puffer létrehozásakor kezdetben 0-ra van állítva. Más szóval, ez a következő olvasandó vagy írandó elem indexe.

A jel a kurzor pozíciójának mentésére szolgál. Miközben egy puffert manipulálunk, a kurzor pozíciója folyamatosan változik, de mindig vissza tudjuk állítani az előzőleg megjelölt pozícióba.

A pufferrel való munkavégzés módszerei

Most nézzük meg a fő metóduskészletet, amely lehetővé teszi, hogy a pufferünkkel (memóriablokkunkkal) dolgozzunk az adatok olvasásához és csatornákba való írásához.

  1. allokáció(int kapacitás) – ez a módszer egy új puffer lefoglalására szolgál a megadott kapacitással. Az allocate() metódus IllegalArgumentException kivételt dob , ha az átadott kapacitás negatív egész szám.

  2. kapacitás() az aktuális puffer kapacitását adja vissza .

  3. position() a kurzor aktuális pozícióját adja vissza. Az olvasási és írási műveletek a kurzort a puffer végére mozgatják. A visszatérési érték mindig kisebb vagy egyenlő a határértékkel.

  4. limit() az aktuális puffer korlátját adja vissza.

  5. mark() a kurzor aktuális pozíciójának megjelölésére (mentésére) szolgál.

  6. A reset() visszaállítja a kurzort az előzőleg megjelölt (mentett) pozícióba.

  7. clear() nullára állítja a pozíciót, és beállítja a kapacitás korlátját. Ez a módszer nem törli a pufferben lévő adatokat. Csak a pozíciót, a határértéket és a jelölést inicializálja újra.

  8. A flip() a puffert írási módból olvasási módba kapcsolja. Ezenkívül beállítja a határértéket az aktuális pozícióra, majd visszaállítja a pozíciót nullára.

  9. read() – A csatorna olvasási metódusa a csatornából a pufferbe írandó adatok, míg a puffer put() metódusa az adatok pufferbe írására szolgál.

  10. write() – A csatorna írási metódusa az adatok pufferből a csatornába írásához, míg a puffer get() metódusa a pufferből való adatok olvasásához.

  11. rewind() visszatekeri a puffert. Ezt a módszert akkor használják, ha újra kell olvasnia a puffert – nullára állítja a pozíciót, és nem módosítja a határértéket.

És most néhány szó a csatornákról.

A Java NIO legfontosabb csatornamegvalósításai a következő osztályok:

  1. FileChannel — Egy csatorna adatok fájlból/fájlba való olvasásához és írásához.

  2. DatagramChannel – Ez az osztály adatokat olvas és ír a hálózaton keresztül UDP-n (User Datagram Protocol) keresztül.

  3. SocketChannel — Adatok olvasására és írására szolgáló csatorna a hálózaton keresztül TCP-n (Transmission Control Protocol) keresztül.

  4. ServerSocketChannel – TCP-kapcsolaton keresztüli adatok olvasására és írására szolgáló csatorna, akárcsak egy webszerver. Minden bejövő kapcsolathoz létrejönegy SocketChannel .

Gyakorlat

Ideje írni néhány sor kódot. Először olvassuk el a fájlt, és jelenítsük meg a tartalmát a konzolon, majd írjunk valamilyen karakterláncot a fájlba.

A kód sok megjegyzést tartalmaz – remélem, segítenek megérteni, hogyan működik minden:


// 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óbálja ki a NIO API -t – imádni fogja!