Terdahulu, kami telah mengenali IO API (Input/Output Application Programming Interface) dan pakej java.io , yang kelasnya terutamanya untuk bekerja dengan strim di Java. Kuncinya di sini ialah konsep aliran .

Hari ini kita akan mula mempertimbangkan API NIO (Input/Output Baharu).

Perbezaan utama antara kedua-dua pendekatan kepada I/O ialah API IO berorientasikan aliran manakala API NIO berorientasikan penimbal. Jadi konsep utama yang perlu difahami ialah penampan dan saluran .

Apakah penimbal dan apakah saluran?

Saluran ialah portal logik yang melaluinya data bergerak masuk dan keluar, manakala penimbal ialah sumber atau destinasi data yang dihantar ini . Semasa output, data yang anda ingin hantar dimasukkan ke dalam penimbal, dan penimbal menghantar data ke saluran. Semasa input, data daripada saluran akan dimasukkan ke dalam penimbal.

Dalam kata lain:

  • penimbal hanyalah blok ingatan di mana kita boleh menulis maklumat dan daripadanya kita boleh membaca maklumat ,
  • saluran ialah pintu masuk yang menyediakan akses kepada peranti I/O seperti fail atau soket.

Saluran sangat serupa dengan aliran dalam pakej java.io. Semua data yang pergi ke mana-mana (atau datang dari mana-mana) mesti melalui objek saluran. Secara umum, untuk menggunakan sistem NIO, anda mendapat saluran kepada entiti I/O dan penimbal untuk menyimpan data. Kemudian anda bekerja dengan penimbal, memasukkan atau mengeluarkan data mengikut keperluan.

Anda boleh bergerak ke hadapan dan ke belakang dalam penampan, iaitu "berjalan" penampan, yang merupakan sesuatu yang anda tidak boleh lakukan dalam aliran. Ini memberikan lebih fleksibiliti semasa memproses data. Dalam perpustakaan standard, penimbal diwakili oleh kelas Penampan abstrak dan beberapa keturunannya:

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

Perbezaan utama antara subkelas ialah jenis data yang mereka simpan — bait , ints , panjang dan jenis data primitif lain.

Sifat penimbal

Penampan mempunyai empat sifat utama. Ini adalah kapasiti, had, kedudukan, dan tanda.

Kapasiti ialah jumlah maksimum data/bait yang boleh disimpan dalam penimbal. Kapasiti penimbal tidak boleh diubah . Setelah penimbal penuh, ia mesti dikosongkan sebelum menulis lebih lanjut kepadanya.

Dalam mod tulis, had penimbal adalah sama dengan kapasitinya, menunjukkan jumlah maksimum data yang boleh ditulis kepada penimbal. Dalam mod baca, had penimbal merujuk kepada jumlah maksimum data yang boleh dibaca daripada penimbal.

Kedudukan menunjukkan kedudukan semasa kursor dalam penimbal. Pada mulanya, ia ditetapkan kepada 0 apabila penimbal dibuat. Dalam erti kata lain, ia adalah indeks elemen seterusnya untuk dibaca atau ditulis.

Tanda digunakan untuk menyimpan kedudukan kursor . Semasa kita memanipulasi penimbal, kedudukan kursor sentiasa berubah, tetapi kita sentiasa boleh mengembalikannya ke kedudukan yang ditandakan sebelum ini.

Kaedah untuk bekerja dengan penimbal

Sekarang mari kita lihat set kaedah utama yang membolehkan kita bekerja dengan penimbal (blok memori) untuk membaca dan menulis data ke dan dari saluran.

  1. allocate(int capacity) — kaedah ini digunakan untuk memperuntukkan penimbal baharu dengan kapasiti yang ditentukan. Kaedah allocate() membuang IllegalArgumentException jika kapasiti yang diluluskan ialah integer negatif.

  2. capacity() mengembalikan kapasiti penampan semasa .

  3. position() mengembalikan kedudukan kursor semasa. Operasi baca dan tulis menggerakkan kursor ke penghujung penimbal. Nilai pulangan sentiasa kurang daripada atau sama dengan had.

  4. limit() mengembalikan had penimbal semasa.

  5. mark() digunakan untuk menandakan (simpan) kedudukan kursor semasa.

  6. reset() mengembalikan kursor ke kedudukan yang telah ditanda (disimpan) sebelumnya.

  7. clear() menetapkan kedudukan kepada sifar dan menetapkan had kepada kapasiti. Kaedah ini tidak mengosongkan data dalam penimbal. Ia hanya memulakan semula kedudukan, had dan tanda.

  8. flip() menukar penimbal daripada mod tulis kepada mod baca. Ia juga menetapkan had kepada kedudukan semasa dan kemudian meletakkan semula kedudukan pada sifar.

  9. read() — Kaedah baca saluran digunakan untuk menulis data dari saluran ke penimbal, manakala kaedah put() penimbal digunakan untuk menulis data ke penimbal.

  10. write() — Kaedah tulis saluran digunakan untuk menulis data daripada penimbal ke saluran, manakala kaedah get() penimbal digunakan untuk membaca data daripada penimbal.

  11. rewind() memundurkan penimbal. Kaedah ini digunakan apabila anda perlu membaca semula penimbal — ia menetapkan kedudukan kepada sifar dan tidak mengubah had.

Dan kini beberapa perkataan tentang saluran.

Pelaksanaan saluran yang paling penting dalam Java NIO ialah kelas berikut:

  1. FileChannel — Saluran untuk membaca dan menulis data dari/ke fail.

  2. DatagramChannel — Kelas ini membaca dan menulis data melalui rangkaian melalui UDP (User Datagram Protocol).

  3. SocketChannel — Saluran untuk membaca dan menulis data melalui rangkaian melalui TCP (Transmission Control Protocol).

  4. ServerSocketChannel — Saluran untuk membaca dan menulis data melalui sambungan TCP, sama seperti pelayan web. SocketChanneldicipta untuk setiap sambungan masuk .

berlatih

Sudah tiba masanya untuk menulis beberapa baris kod. Mula-mula, mari baca fail dan paparkan kandungannya pada konsol, kemudian tulis beberapa rentetan pada fail itu.

Kod ini mengandungi banyak komen — Saya harap ia akan membantu anda memahami cara semuanya berfungsi:


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

Cuba API NIO — anda pasti menyukainya!