ก่อนหน้านี้ เราได้รู้จัก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 เมื่อสร้างบัฟเฟอร์ กล่าวอีกนัยหนึ่งคือดัชนีขององค์ประกอบถัดไปที่จะอ่านหรือเขียน
เครื่องหมายนี้ใช้เพื่อบันทึกตำแหน่งเคอร์เซอร์ ขณะที่เราจัดการบัฟเฟอร์ ตำแหน่งเคอร์เซอร์จะเปลี่ยนแปลงตลอดเวลา แต่เราสามารถกลับไปที่ตำแหน่งที่ทำเครื่องหมายไว้ก่อนหน้านี้ได้เสมอ
วิธีการทำงานกับบัฟเฟอร์
ทีนี้มาดูชุดเมธอดหลักที่ให้เราทำงานกับบัฟเฟอร์ (บล็อกหน่วยความจำ) สำหรับการอ่านและเขียนข้อมูลไปยังและจากแชนเนล
-
จัดสรร (ความจุ int) - วิธีนี้ใช้เพื่อจัดสรรบัฟเฟอร์ใหม่ด้วยความจุที่ระบุ วิธี การallocation()พ่นIllegalArgumentExceptionหากความจุที่ส่งผ่านเป็นจำนวนเต็มลบ
-
ความจุ () ส่งคืน ความจุของบัฟเฟอร์ปัจจุบัน
-
Position()ส่งคืนตำแหน่งเคอร์เซอร์ปัจจุบัน การดำเนินการอ่านและเขียนจะเลื่อนเคอร์เซอร์ไปที่จุดสิ้นสุดของบัฟเฟอร์ ค่าส่งกลับจะน้อยกว่าหรือเท่ากับขีดจำกัดเสมอ
-
limit()ส่งกลับขีดจำกัดของบัฟเฟอร์ปัจจุบัน
-
เครื่องหมาย ()ใช้เพื่อทำเครื่องหมาย (บันทึก) ตำแหน่งเคอร์เซอร์ปัจจุบัน
-
รีเซ็ต ()ส่งคืนเคอร์เซอร์ไปยังตำแหน่งที่ทำเครื่องหมายไว้ก่อนหน้านี้ (บันทึก)
-
clear()กำหนดตำแหน่งเป็นศูนย์และกำหนดขีดจำกัดของความจุ วิธีนี้ไม่ได้ล้างข้อมูลในบัฟเฟอร์ โดยจะเริ่มต้นตำแหน่ง ขีดจำกัด และเครื่องหมายใหม่เท่านั้น
-
flip()เปลี่ยนบัฟเฟอร์จากโหมดเขียนเป็นโหมดอ่าน นอกจากนี้ยังกำหนดขีดจำกัดให้กับตำแหน่งปัจจุบัน จากนั้นจึงเปลี่ยนตำแหน่งกลับเป็นศูนย์
-
read() — เมธอด read ของแชนเนลใช้เพื่อเขียนข้อมูลจากแชนเนลไปยังบัฟเฟอร์ ในขณะที่เมธอดput() ของบัฟเฟอร์ ใช้เพื่อเขียนข้อมูลไปยังบัฟเฟอร์
-
การเขียน() — วิธีการเขียนของแชนเนลใช้เพื่อเขียนข้อมูลจากบัฟเฟอร์ไปยังแชนเนล ในขณะที่เมธอดget() ของบัฟเฟอร์ ใช้เพื่ออ่านข้อมูลจากบัฟเฟอร์
-
ย้อนกลับ ()ย้อนกลับบัฟเฟอร์ วิธีนี้ใช้เมื่อคุณต้องการอ่านบัฟเฟอร์อีกครั้ง โดยกำหนดตำแหน่งเป็นศูนย์และไม่เปลี่ยนขีดจำกัด
และตอนนี้คำสองสามคำเกี่ยวกับช่อง
การใช้งานช่องทางที่สำคัญที่สุดใน Java NIO คือคลาสต่อไปนี้:
-
FileChannel — ช่องสำหรับอ่านและเขียนข้อมูลจาก/ไปยังไฟล์
-
DatagramChannel — คลาสนี้อ่านและเขียนข้อมูลผ่านเครือข่ายผ่าน UDP (User Datagram Protocol)
-
SocketChannel — ช่องสำหรับอ่านและเขียนข้อมูลผ่านเครือข่ายผ่าน TCP (Transmission Control Protocol)
-
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แล้วคุณจะหลงรักมัน!