Mas maaga, nakilala namin ang IO API (Input/Output Application Programming Interface) at ang java.io package, na ang mga klase ay pangunahing para sa pagtatrabaho sa mga stream sa Java. Ang susi dito ay ang konsepto ng isang stream .

Ngayon ay sisimulan nating isaalang-alang ang NIO API (Bagong Input/Output).

Ang pangunahing pagkakaiba sa pagitan ng dalawang diskarte sa I/O ay ang IO API ay stream-oriented habang ang NIO API ay buffer-oriented. Kaya't ang mga pangunahing konsepto na dapat maunawaan ay mga buffer at channel .

Ano ang buffer at ano ang channel?

Ang channel ay isang lohikal na portal kung saan gumagalaw ang data papasok at palabas, habang ang buffer ay ang pinagmulan o destinasyon ng ipinadalang data na ito. Sa panahon ng output, ang data na gusto mong ipadala ay inilalagay sa isang buffer, at ipinapasa ng buffer ang data sa channel. Sa panahon ng pag-input, ang data mula sa channel ay inilalagay sa buffer.

Sa ibang salita:

  • ang buffer ay simpleng bloke ng memorya kung saan maaari tayong sumulat ng impormasyon at kung saan maaari tayong magbasa ng impormasyon,
  • ang channel ay isang gateway na nagbibigay ng access sa mga I/O device gaya ng mga file o socket.

Ang mga channel ay halos kapareho sa mga stream sa java.io package. Ang lahat ng data na napupunta kahit saan (o nagmumula sa kahit saan) ay dapat dumaan sa isang channel object. Sa pangkalahatan, para magamit ang NIO system, makakakuha ka ng channel sa isang I/O entity at isang buffer para sa pag-iimbak ng data. Pagkatapos ay gagana ka sa buffer, pag-input o pag-output ng data kung kinakailangan.

Maaari kang sumulong at paatras sa isang buffer, ibig sabihin, "lakad" ang buffer, na isang bagay na hindi mo magagawa sa mga stream. Nagbibigay ito ng higit na kakayahang umangkop kapag nagpoproseso ng data. Sa karaniwang library, ang mga buffer ay kinakatawan ng abstract na klase ng Buffer at ilan sa mga inapo nito:

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

Ang pangunahing pagkakaiba sa pagitan ng mga subclass ay ang uri ng data na iniimbak nila — bytes , ints , longs at iba pang primitive na uri ng data.

Mga katangian ng buffer

Ang buffer ay may apat na pangunahing katangian. Ito ay ang kapasidad, limitasyon, posisyon, at marka.

Ang kapasidad ay ang maximum na dami ng data/bytes na maaaring maimbak sa buffer. Hindi mababago ang kapasidad ng buffer . Kapag puno na ang buffer, dapat itong i-clear bago sumulat ng higit pa dito.

Sa write mode, ang limitasyon ng buffer ay kapareho ng kapasidad nito, na nagpapahiwatig ng maximum na dami ng data na maaaring isulat sa buffer. Sa read mode, ang limitasyon ng buffer ay tumutukoy sa maximum na dami ng data na mababasa mula sa buffer.

Ang posisyon ay nagpapahiwatig ng kasalukuyang posisyon ng cursor sa buffer. Sa una, ito ay nakatakda sa 0 kapag ginawa ang buffer. Sa madaling salita, ito ang index ng susunod na elemento na babasahin o isusulat.

Ginagamit ang marka upang i-save ang posisyon ng cursor. Habang minamanipula namin ang isang buffer, patuloy na nagbabago ang posisyon ng cursor, ngunit maaari naming ibalik ito palagi sa dating minarkahang posisyon.

Mga pamamaraan para sa pagtatrabaho sa isang buffer

Ngayon tingnan natin ang pangunahing hanay ng mga pamamaraan na nagpapahintulot sa amin na magtrabaho kasama ang aming buffer (block ng memorya) para sa pagbabasa at pagsusulat ng data papunta at mula sa mga channel.

  1. allocate(int capacity) — ang paraang ito ay ginagamit upang maglaan ng bagong buffer na may tinukoy na kapasidad. Ang paraan ng allocate() ay nagtatapon ng IllegalArgumentException kung ang naipasa na kapasidad ay isang negatibong integer.

  2. capacity() ay nagbabalik ng kasalukuyang buffer's capacity .

  3. position() ay nagbabalik ng kasalukuyang posisyon ng cursor. Ang mga operasyon sa pagbasa at pagsulat ay naglilipat ng cursor sa dulo ng buffer. Ang return value ay palaging mas mababa sa o katumbas ng limitasyon.

  4. limit() ibinabalik ang kasalukuyang limitasyon ng buffer.

  5. Ang mark() ay ginagamit upang markahan (i-save) ang kasalukuyang posisyon ng cursor.

  6. reset() ibinabalik ang cursor sa dating minarkahan (naka-save) na posisyon.

  7. Ang clear() ay nagtatakda ng posisyon sa zero at nagtatakda ng limitasyon sa kapasidad. Hindi nililinis ng pamamaraang ito ang data sa buffer. Ire-reinitialize lang nito ang posisyon, limitasyon, at marka.

  8. flip() inililipat ang buffer mula sa write mode patungo sa read mode. Itinatakda din nito ang limitasyon sa kasalukuyang posisyon at pagkatapos ay ibabalik ang posisyon sa zero.

  9. read() — Ang paraan ng pagbabasa ng channel ay ginagamit upang magsulat ng data mula sa channel patungo sa buffer, habang ang put() na paraan ng buffer ay ginagamit upang magsulat ng data sa buffer.

  10. write() — Ang paraan ng pagsulat ng channel ay ginagamit upang magsulat ng data mula sa buffer patungo sa channel, habang ang paraan ng get() ng buffer ay ginagamit upang basahin ang data mula sa buffer.

  11. rewind() rewind ang buffer. Ginagamit ang pamamaraang ito kapag kailangan mong basahin muli ang buffer — itinatakda nito ang posisyon sa zero at hindi binabago ang limitasyon.

At ngayon ng ilang mga salita tungkol sa mga channel.

Ang pinakamahalagang pagpapatupad ng channel sa Java NIO ay ang mga sumusunod na klase:

  1. FileChannel — Isang channel para sa pagbabasa at pagsusulat ng data mula/sa isang file.

  2. DatagramChannel — Ang klase na ito ay nagbabasa at nagsusulat ng data sa network sa pamamagitan ng UDP (User Datagram Protocol).

  3. SocketChannel — Isang channel para sa pagbabasa at pagsusulat ng data sa network sa pamamagitan ng TCP (Transmission Control Protocol).

  4. ServerSocketChannel — Isang channel para sa pagbabasa at pagsusulat ng data sa mga koneksyon sa TCP, tulad ng ginagawa ng isang web server. Ang isang SocketChannel ay nilikha para sa bawat papasok na koneksyon.

Magsanay

Oras na para magsulat ng ilang linya ng code. Una, basahin natin ang file at ipakita ang mga nilalaman nito sa console, at pagkatapos ay magsulat ng ilang string sa file.

Ang code ay naglalaman ng maraming komento — sana ay tulungan ka nilang maunawaan kung paano gumagana ang lahat:


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

Subukan ang NIO API — magugustuhan mo ito!