ก่อนหน้านี้ เราได้รู้จักIO API (Input/Output Application Programming Interface) และ แพ็คเกจ java.ioซึ่งมีคลาสสำหรับการทำงานกับสตรีมใน Java เป็นหลัก กุญแจสำคัญคือแนวคิดของสตรี

วันนี้เราจะเริ่มพิจารณาNIO API (New Input/Output)

ข้อแตกต่างที่สำคัญระหว่างสองแนวทางสำหรับ I/O คือ IO API เป็นแบบสตรีม ในขณะที่ NIO API เป็นแบบบัฟเฟอร์ ดังนั้นแนวคิดหลักที่ต้องทำความเข้าใจคือบัฟเฟอร์และแชนเน

บัฟเฟอร์คืออะไรและช่องสัญญาณคืออะไร

แชนเนลคือพอร์ทัลโลจิคัลที่ข้อมูลเคลื่อนเข้าและออก ในขณะที่บัฟเฟอร์คือต้นทางหรือปลายทางของข้อมูลที่ส่งนี้ ระหว่างเอาต์พุต ข้อมูลที่คุณต้องการส่งจะถูกใส่ลงในบัฟเฟอร์ และบัฟเฟอร์จะส่งข้อมูลไปยังแชนเนล ในระหว่างการป้อนข้อมูล ข้อมูลจากช่องจะถูกใส่ลงในบัฟเฟอร์

กล่าวอีกนัยหนึ่ง:

  • บัฟเฟอร์เป็นเพียงบล็อกของหน่วยความจำที่เราสามารถเขียนข้อมูลและอ่านข้อมูลได้
  • แชนเนลเป็นเกตเวย์ที่ให้การเข้าถึงอุปกรณ์ I/O เช่น ไฟล์หรือซ็อกเก็ต

แชนเนลมีความคล้ายคลึงกับสตรีมในแพ็คเกจ java.io ข้อมูลทั้งหมดที่ไปได้ทุกที่ (หรือมาจากที่ใดก็ได้) จะต้องผ่านอ็อบเจกต์แชนเนล โดยทั่วไป ในการใช้ระบบ NIO คุณจะได้รับช่องทางไปยังเอนทิตี I/O และบัฟเฟอร์สำหรับจัดเก็บข้อมูล จากนั้นคุณก็ทำงานกับบัฟเฟอร์ ป้อนข้อมูลหรือส่งออกข้อมูลตามต้องการ

คุณสามารถเดินหน้าและถอยหลังในบัฟเฟอร์ กล่าวคือ "เดิน" บัฟเฟอร์ ซึ่งเป็นสิ่งที่คุณไม่สามารถทำได้ในสตรีม สิ่งนี้ทำให้มีความยืดหยุ่นมากขึ้นในการประมวลผลข้อมูล ในไลบรารีมาตรฐาน บัฟเฟอร์จะแสดงด้วยคลาสนามธรรมบัฟเฟอร์และคลาสย่อยอีกหลายคลาส:

  • ไบต์บัฟเฟอร์
  • ชาร์บัฟเฟอร์
  • ชอร์ตบัฟเฟอร์
  • IntBuffer
  • FloatBuffer
  • ดับเบิ้ลบัฟเฟอร์
  • ลองบัฟเฟอร์

ความแตกต่างที่สำคัญระหว่างคลาสย่อยคือประเภทข้อมูลที่จัดเก็บ — bytes , ints , longsและประเภทข้อมูลดั้งเดิมอื่นๆ

คุณสมบัติของบัฟเฟอร์

บัฟเฟอร์มีคุณสมบัติหลักสี่ประการ เหล่านี้คือความจุ ขีดจำกัด ตำแหน่ง และเครื่องหมาย

ความจุคือจำนวนข้อมูล/ไบต์สูงสุดที่สามารถจัดเก็บในบัฟเฟอร์ ไม่สามารถเปลี่ยนความจุของบัฟเฟอร์ได้ เมื่อบัฟเฟอร์เต็มแล้ว จะต้องล้างบัฟเฟอร์ก่อนที่จะเขียนเพิ่มเติม

ในโหมดการเขียน ขีดจำกัดของบัฟเฟอร์จะเหมือนกับความจุ ซึ่งระบุจำนวนข้อมูลสูงสุดที่สามารถเขียนไปยังบัฟเฟอร์ได้ ในโหมดอ่าน ขีดจำกัดของบัฟเฟอร์หมายถึงจำนวนข้อมูลสูงสุดที่สามารถอ่านได้จากบัฟเฟอร์

ตำแหน่ง ระบุ ตำแหน่งปัจจุบันของเคอร์เซอร์ในบัฟเฟอร์ ในขั้นต้น จะถูกตั้งค่าเป็น 0 เมื่อสร้างบัฟเฟอร์ กล่าวอีกนัยหนึ่งคือดัชนีขององค์ประกอบถัดไปที่จะอ่านหรือเขียน

เครื่องหมายนี้ใช้เพื่อบันทึกตำแหน่งเคอร์เซอร์ ขณะที่เราจัดการบัฟเฟอร์ ตำแหน่งเคอร์เซอร์จะเปลี่ยนแปลงตลอดเวลา แต่เราสามารถกลับไปที่ตำแหน่งที่ทำเครื่องหมายไว้ก่อนหน้านี้ได้เสมอ

วิธีการทำงานกับบัฟเฟอร์

ทีนี้มาดูชุดเมธอดหลักที่ให้เราทำงานกับบัฟเฟอร์ (บล็อกหน่วยความจำ) สำหรับการอ่านและเขียนข้อมูลไปยังและจากแชนเนล

  1. จัดสรร (ความจุ int) - วิธีนี้ใช้เพื่อจัดสรรบัฟเฟอร์ใหม่ด้วยความจุที่ระบุ วิธี การallocation()พ่นIllegalArgumentExceptionหากความจุที่ส่งผ่านเป็นจำนวนเต็มลบ

  2. ความจุ () ส่งคืน ความจุของบัฟเฟอร์ปัจจุบัน

  3. Position()ส่งคืนตำแหน่งเคอร์เซอร์ปัจจุบัน การดำเนินการอ่านและเขียนจะเลื่อนเคอร์เซอร์ไปที่จุดสิ้นสุดของบัฟเฟอร์ ค่าส่งกลับจะน้อยกว่าหรือเท่ากับขีดจำกัดเสมอ

  4. limit()ส่งกลับขีดจำกัดของบัฟเฟอร์ปัจจุบัน

  5. เครื่องหมาย ()ใช้เพื่อทำเครื่องหมาย (บันทึก) ตำแหน่งเคอร์เซอร์ปัจจุบัน

  6. รีเซ็ต ()ส่งคืนเคอร์เซอร์ไปยังตำแหน่งที่ทำเครื่องหมายไว้ก่อนหน้านี้ (บันทึก)

  7. clear()กำหนดตำแหน่งเป็นศูนย์และกำหนดขีดจำกัดของความจุ วิธีนี้ไม่ได้ล้างข้อมูลในบัฟเฟอร์ โดยจะเริ่มต้นตำแหน่ง ขีดจำกัด และเครื่องหมายใหม่เท่านั้น

  8. flip()เปลี่ยนบัฟเฟอร์จากโหมดเขียนเป็นโหมดอ่าน นอกจากนี้ยังกำหนดขีดจำกัดให้กับตำแหน่งปัจจุบัน จากนั้นจึงเปลี่ยนตำแหน่งกลับเป็นศูนย์

  9. read() — เมธอด read ของแชนเนลใช้เพื่อเขียนข้อมูลจากแชนเนลไปยังบัฟเฟอร์ ในขณะที่เมธอดput() ของบัฟเฟอร์ ใช้เพื่อเขียนข้อมูลไปยังบัฟเฟอร์

  10. การเขียน() — วิธีการเขียนของแชนเนลใช้เพื่อเขียนข้อมูลจากบัฟเฟอร์ไปยังแชนเนล ในขณะที่เมธอดget() ของบัฟเฟอร์ ใช้เพื่ออ่านข้อมูลจากบัฟเฟอร์

  11. ย้อนกลับ ()ย้อนกลับบัฟเฟอร์ วิธีนี้ใช้เมื่อคุณต้องการอ่านบัฟเฟอร์อีกครั้ง โดยกำหนดตำแหน่งเป็นศูนย์และไม่เปลี่ยนขีดจำกัด

และตอนนี้คำสองสามคำเกี่ยวกับช่อง

การใช้งานช่องทางที่สำคัญที่สุดใน Java NIO คือคลาสต่อไปนี้:

  1. FileChannel — ช่องสำหรับอ่านและเขียนข้อมูลจาก/ไปยังไฟล์

  2. DatagramChannel — คลาสนี้อ่านและเขียนข้อมูลผ่านเครือข่ายผ่าน UDP (User Datagram Protocol)

  3. SocketChannel — ช่องสำหรับอ่านและเขียนข้อมูลผ่านเครือข่ายผ่าน TCP (Transmission Control Protocol)

  4. ServerSocketChannel — ช่องสำหรับอ่านและเขียนข้อมูลผ่านการเชื่อมต่อ TCP เช่นเดียวกับเว็บเซิร์ฟเวอร์ SocketChannelถูกสร้างขึ้นสำหรับการเชื่อมต่อขาเข้าแต่ละครั้ง

ฝึกฝน

ได้เวลาเขียนโค้ดสองสามบรรทัดแล้ว ขั้นแรก ให้อ่านไฟล์และแสดงเนื้อหาบนคอนโซล จากนั้นเขียนสตริงลงในไฟล์

รหัสประกอบด้วยความคิดเห็นจำนวนมาก — ฉันหวังว่าพวกเขาจะช่วยให้คุณเข้าใจว่าทุกอย่างทำงานอย่างไร:


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

ลองใช้NIO APIแล้วคุณจะหลงรักมัน!