คำนำ
ในบทความก่อนหน้านี้เราแนะนำประเภทไฟล์ของไฟล์ดิสก์นามธรรม มันถูกใช้เพื่ออธิบายไฟล์ดิสก์หรือไดเรกทอรีอย่างเป็นนามธรรม แต่ไม่มีความสามารถในการเข้าถึงและแก้ไขเนื้อหาของไฟล์
Java IO Stream เป็นการออกแบบที่ใช้ในการอ่านและเขียนเนื้อหาไฟล์ สามารถกรอกข้อมูลการถ่ายโอนข้อมูลของไฟล์ดิสก์เอาต์พุตไปยังหน่วยความจำหรือข้อมูลหน่วยความจำเอาต์พุตไปยังไฟล์ดิสก์
การออกแบบลำธาร Java IO ไม่สมบูรณ์แบบ มันได้ออกแบบคลาสจำนวนมากซึ่งเพิ่มความเข้าใจของเราเกี่ยวกับสตรีม IO แต่มีเพียงสองหมวดหมู่ที่สำคัญเท่านั้น: หนึ่งคือสตรีมไบต์สำหรับไฟล์ไบนารีและอีกประเภทหนึ่งคือสตรีมอักขระสำหรับไฟล์ข้อความ ในบทความนี้ก่อนอื่นเราจะได้เรียนรู้หลักการและสถานการณ์การใช้งานของลำธารไบต์ประเภทที่เกี่ยวข้อง ประเภทสตรีมเฉพาะส่วนใหญ่เกี่ยวข้องมีดังนี้:
คลาสพื้นฐานไบต์สตรีมอินพุต/เอาท์พุทสตรีม
InputStream และ OutputStream เป็นคลาสพื้นฐานสำหรับการอ่านสตรีมไบต์และการเขียนสตรีมไบต์ตามลำดับ สตรีมที่เกี่ยวข้องกับไบต์ทั้งหมดจะต้องสืบทอดจากพวกเขา ในฐานะคลาสนามธรรมพวกเขายังกำหนดการอ่านและเขียนขั้นพื้นฐานที่สุด มาดูกันเถอะ:
ใช้อินพุทสตรีมเป็นตัวอย่าง:
บทคัดย่อสาธารณะ int อ่าน () โยน ioexception;
นี่เป็นวิธีที่เป็นนามธรรมและไม่ได้ให้การใช้งานเริ่มต้นซึ่งกำหนดให้ต้องใช้คลาสย่อย วัตถุประสงค์ของวิธีนี้คือการส่งคืนไบต์ถัดไปของไฟล์ปัจจุบันสำหรับคุณ
แน่นอนคุณจะพบว่าค่าส่งคืนของวิธีนี้ได้รับโดยใช้ประเภทจำนวนเต็ม "int" ดังนั้นทำไมไม่ใช้ "ไบต์"?
ก่อนอื่นค่าที่ส่งคืนโดยวิธีการอ่านจะต้องเป็นไบนารีแปดบิตและช่วงเวลาของค่าที่สามารถทำได้โดยไบนารีแปดบิตคือ: "0000 0000, 1111 1111" นั่นคือช่วง [-128, 127]
วิธีการอ่านยังระบุด้วยว่าเมื่อไฟล์ถูกอ่านไปยังจุดสิ้นสุดนั่นคือไฟล์ไม่มีไบต์ถัดไปสำหรับการอ่านค่า -1 จะถูกส่งคืน ดังนั้นหากใช้ไบต์เป็นประเภทค่าส่งคืนเมื่อวิธีการส่งคืน A -1 เราควรพิจารณาว่านี่เป็นเนื้อหาข้อมูลในไฟล์หรือส่วนท้ายของสตรีมหรือไม่?
ประเภท INT มีสี่ไบต์และสามไบต์ในบิตสูงทั้งหมดเป็น 0 เราใช้บิตบิตต่ำสุดเท่านั้น เมื่อพบจุดสิ้นสุดของธงสตรีมมันจะส่งคืน -1 (32 1S) แสดงโดยสี่ไบต์ซึ่งแตกต่างจากค่า -1 (24 0 + 8 1S) ตามธรรมชาติ
ถัดไปเป็นวิธีการอ่าน แต่ InputStream มีการใช้งานเริ่มต้น:
Public Int Read (byte b []) พ่น IOException {return read (b, 0, b.length);} public int read (byte b [], int ปิด, int len) โยน ioexception {// เพื่อไม่ให้ยาวเกินไปทั้งสองวิธีนี้เหมือนกัน วิธีแรกเป็นรูปแบบพิเศษของวิธีที่สองซึ่งอนุญาตให้อาร์เรย์ของไบต์ส่งผ่านและต้องการโปรแกรมเพื่อเติมไบต์ที่อ่านในไฟล์เริ่มต้นจากตำแหน่งดัชนีอาร์เรย์ 0 เพื่อเติมจำนวนไบต์ตามความยาวของอาร์เรย์
วิธีที่สองนั้นกว้างขึ้นเล็กน้อยซึ่งช่วยให้คุณสามารถระบุตำแหน่งเริ่มต้นและจำนวนทั้งหมดของไบต์
มีวิธีอื่นอีกหลายวิธีใน InputStream ซึ่งโดยทั่วไปไม่ได้ใช้ในรายละเอียด ลองมาดูกันในเวลาสั้น ๆ
วิธีการทำเครื่องหมายจะทำเครื่องหมายธงที่ตำแหน่งการอ่านสตรีมปัจจุบันและวิธีการรีเซ็ตจะรีเซ็ตตัวชี้อ่านเป็นธง
ในความเป็นจริงมันเป็นไปไม่ได้ที่จะรีเซ็ตการอ่านกลับสำหรับการอ่านไฟล์ แต่โดยทั่วไปทั้งหมดไบต์ระหว่างตำแหน่งธงและจุดรีเซ็ตจะถูกบันทึกชั่วคราว เมื่อมีการเรียกใช้วิธีการรีเซ็ตจริง ๆ แล้วจะอ่านซ้ำจากชุดไบต์ชั่วคราวที่บันทึกไว้ดังนั้น readlimit จะใช้เพื่อ จำกัด ความจุแคชสูงสุด
วิธีการ Marksupported ใช้เพื่อตรวจสอบว่าสตรีมปัจจุบันรองรับการดำเนินการอ่าน "ทางเลือก" นี้หรือไม่
OutputStream และ InputStream คล้ายกันยกเว้นว่ามีการเขียนและอีกอันหนึ่งถูกอ่าน เราจะไม่ทำซ้ำที่นี่
File Byte Stream FileInput/OutputStream
เรายังคงมุ่งเน้นไปที่ FileInputStream และ FileOutputStream นั้นคล้ายคลึงกัน
ขั้นแรก FileInputStream มีตัวสร้างต่อไปนี้เพื่อยกตัวอย่างวัตถุ:
Public FileInputStream (ชื่อสตริง) พ่น filenotFoundException {this (name! = null? ไฟล์ใหม่ (ชื่อ): null);} Public FileInputStream (ไฟล์ไฟล์) พ่น filenotFoundException {string name = (ไฟล์! = null? file.getPath (): null); SecurityManager Security = System.getSecurityManager (); if (ความปลอดภัย! = null) {security.checkread (ชื่อ); } if (name == null) {โยน nullpointerexception ใหม่ (); } if (file.isinvalid ()) {โยน FilenotFoundException ใหม่ ("เส้นทางไฟล์ไม่ถูกต้อง"); } fd = ใหม่ FileDescriptor (); fd.attach (นี่); เส้นทาง = ชื่อ; เปิด (ชื่อ);}ตัวสร้างทั้งสองนี้มีความเหมือนกันในอดีตเป็นรูปแบบพิเศษของหลัง ในความเป็นจริงอย่าดูวิธีหลังซึ่งส่วนใหญ่เป็นเพียงการตรวจสอบความปลอดภัย แกนกลางเป็นวิธีเปิดซึ่งใช้ในการเปิดไฟล์
ส่วนใหญ่ตัวสร้างทั้งสองนี้หากไฟล์ไม่มีอยู่หรือเส้นทางไฟล์และชื่อนั้นผิดกฎหมายจะมีการโยน FilenotFoundException
โปรดจำไว้ว่าเราบอกว่ามีวิธีการอ่านแบบนามธรรมในคลาสพื้นฐานอินพุตสตรีมที่ต้องการคลาสย่อยทั้งหมดที่จะนำไปใช้และ FileInputStream ถูกนำมาใช้โดยใช้วิธีการในท้องถิ่น:
Public int read () พ่น IOException {return read0 ();} private native int read0 () โยน ioexception;เราไม่มีวิธีสำรวจการใช้งานเฉพาะของ Read0 ในขณะนี้ แต่คุณต้องชัดเจนว่าฟังก์ชั่นของวิธีการอ่านนี้ใช้เพื่อส่งคืนไบต์ถัดไปในสตรีมและส่งคืน -1 หมายความว่ามันถูกอ่านไปจนถึงจุดสิ้นสุดของไฟล์และไม่มีไบต์ให้อ่าน
นอกจากนี้ยังมีวิธีการที่เกี่ยวข้องกับการอ่านอื่น ๆ ใน FileInputStream แต่ส่วนใหญ่ใช้โดยใช้วิธีการในท้องถิ่น มาดูกันเถอะ:
วิธีการภายในของ FileInputStream นั้นเป็นแบบนี้และมีวิธีการขั้นสูงและซับซ้อนบางอย่างที่เราไม่สามารถใช้ในช่วงเวลานั้นได้ เราจะเรียนรู้ในภายหลัง มาดูตัวอย่างการอ่านไฟล์สั้น ๆ :
โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น IOException {fileInputStream input = new FileInputStream ("C: //users//yanga//desktop//test.txt"); ไบต์ [] บัฟเฟอร์ = ไบต์ใหม่ [1024]; int len = input.read (บัฟเฟอร์); string str = สตริงใหม่ (บัฟเฟอร์); System.out.println (str); System.out.println (Len); input.close ();}ผลลัพธ์ผลลัพธ์นั้นง่ายมาก มันจะพิมพ์เนื้อหาในไฟล์ทดสอบของเราและจำนวนไบต์จริงที่อ่านออก แต่นักเรียนที่ระมัดระวังจะทราบว่าคุณจะมั่นใจได้อย่างไรว่าเนื้อหาในไฟล์ทดสอบจะไม่เกิน 1024 ไบต์ได้อย่างไร
เพื่อที่จะอ่านเนื้อหาของไฟล์ได้อย่างเต็มที่วิธีหนึ่งคือการกำหนดบัฟเฟอร์ที่มีขนาดใหญ่พอที่จะคาดหวังว่าจะเก็บเนื้อหาทั้งหมดของไฟล์ให้มากที่สุด
วิธีนี้ไม่เป็นที่พึงปรารถนาอย่างเห็นได้ชัดเพราะเป็นไปไม่ได้ที่เราจะตระหนักถึงขนาดที่แท้จริงของไฟล์ที่จะอ่าน มันเป็นทางออกที่แย่มากในการสร้างอาร์เรย์ไบต์ขนาดใหญ่
วิธีที่สองคือการใช้สตรีมอาร์เรย์ไบต์แบบไดนามิกของเราซึ่งสามารถปรับขนาดของอาร์เรย์ไบต์ภายในแบบไดนามิกแบบไดนามิกเพื่อให้แน่ใจว่ามีความสามารถที่เหมาะสมซึ่งเราจะแนะนำรายละเอียดในภายหลัง
เกี่ยวกับ FileOutputStream สิ่งหนึ่งที่จะเน้นคือตัวสร้างซึ่งมีสองตัวสร้างต่อไปนี้:
Public FileOutputStream (ชื่อสตริง, Boolean Append) Public FileOutputStream (ไฟล์ไฟล์, Boolean Append)
ต่อท้ายพารามิเตอร์ระบุว่าการดำเนินการเขียนของสตรีมนี้ถูกเขียนทับหรือต่อท้ายหรือต่อท้ายหมายถึงท้ายวิธีการที่ถูกเขียนทับ
ByteArrayInput/OutputStream
"สตรีมอาร์เรย์ไบต์" ที่เรียกว่าเป็นสตรีมที่ทำงานรอบอาร์เรย์ไบต์ มันไม่ได้อ่านและเขียนสตรีมไปยังไฟล์เช่นสตรีมอื่น ๆ
แม้ว่าสตรีมอาร์เรย์ไบต์ไม่ใช่สตรีมที่ใช้ไฟล์ แต่ก็ยังคงเป็นสตรีมที่สำคัญมากเนื่องจากอาร์เรย์ไบต์ที่ห่อหุ้มอยู่ภายในไม่ได้รับการแก้ไข แต่ขยายได้แบบไดนามิกและมักจะขึ้นอยู่กับสถานการณ์บางอย่างซึ่งเหมาะสมมาก
ByteArrayInputStream เป็นสตรีมของอาร์เรย์อ่านไบต์ที่สามารถสร้างอินสแตนซ์โดยตัวสร้างต่อไปนี้:
ได้รับการปกป้องไบต์ buf []; การป้องกัน int pos; จำนวน int ที่ได้รับการป้องกัน; ByteArrayInputStream (byte buf []) {this.buf = buf; this.pos = 0; this.count = buf.length;} public byteArrayInputStream (byte buf [], int offset, ความยาว int)BUF เป็นอาร์เรย์ไบต์ที่ห่อหุ้มอยู่ภายใน ByteArrayInputStream การดำเนินการอ่านทั้งหมดของ ByteArrayInputStream หมุนรอบมัน
ดังนั้นเมื่อสร้างอินสแตนซ์วัตถุ ByteArrayInputStream อย่างน้อยหนึ่งอาร์เรย์ไบต์เป้าหมายจะถูกส่งผ่าน
แอตทริบิวต์ POS ใช้เพื่อบันทึกตำแหน่งของการอ่านกระแสปัจจุบันและนับบันทึกตำแหน่งหลังของดัชนีไบต์ที่ถูกต้องสุดท้ายของอาร์เรย์ไบต์เป้าหมาย
หลังจากทำความเข้าใจกับสิ่งนี้มันไม่ยากที่จะอ่านวิธีต่าง ๆ ในการอ่าน:
// อ่าน BYTE Public Synchronized Int Read () {return (pos <count)? (buf [pos ++] & 0xff): -1;} // อ่าน len bytes และวางไว้ใน byte array b สาธารณะที่ซิงโครไนซ์ int อ่าน (byte b [], int ปิด, int len) {// เดียวกันนอกจากนี้ ByteArrayInputStream ยังใช้การดำเนินการ "ทำซ้ำ"
เครื่องหมายโมฆะสาธารณะ (int readaheadlimit) {mark = pos;} การรีเซ็ตโมฆะแบบซิงโครไนซ์สาธารณะ () {pos = mark;}เนื่องจาก ByTeArrayInputStream ขึ้นอยู่กับอาร์เรย์ไบต์การดำเนินการอ่านซ้ำทั้งหมดจึงง่ายต่อการใช้งานและเพียงพอที่จะนำไปใช้ตามดัชนี
ByteArrayOutputStream เป็นสตรีมอาร์เรย์ไบต์ที่เขียนขึ้น การใช้งานจำนวนมากยังคงมีลักษณะของตัวเอง มาดูกันกันเถอะ
ก่อนอื่นต้องมีคุณสมบัติทั้งสองนี้:
ได้รับการปกป้องไบต์ buf []; // การนับที่นี่แสดงถึงจำนวนไบต์ที่ถูกต้องในการนับ int ที่ได้รับการป้องกัน BUF;
ตัวสร้าง:
Public ByteArrayOutputStream () {this (32);} public byteArrayOutputStream (ขนาด int) {ถ้า (ขนาด <0) {โยน unlegalArgumentException ใหม่ ("ขนาดเริ่มต้นลบ:"+ ขนาด); } buf = ไบต์ใหม่ [ขนาด];}ภารกิจหลักของตัวสร้างคือการเริ่มต้น BYTE ARRAY BIF ภายใน BUF ช่วยให้คุณสามารถผ่านขนาดเพื่อ จำกัด ขนาดอาร์เรย์ไบต์ที่เริ่มต้นได้อย่างชัดเจนมิฉะนั้นความยาวเริ่มต้นจะเป็น 32
เขียนเนื้อหาไปยัง ByteArrayOutputStream จากภายนอก:
การเขียนโมฆะแบบซิงโครไนซ์สาธารณะ (int b) {ensurecapacity (นับ + 1); buf [count] = (ไบต์) B; นับ + = 1;} โมฆะที่ซิงโครไนซ์สาธารณะเขียน (ไบต์ b [], int ปิด, int len) {ถ้า ((ปิด <0) || (ปิด> b.length) || (len <0) || (ปิด + + len) - b.length> 0)) } ensureCapacity (count + len); System.arraycopy (B, Off, Buf, Count, Len); นับ += len;}เมื่อเห็นว่าขั้นตอนแรกของการดำเนินการเขียนทั้งหมดคือการเรียกใช้วิธีการ ensurecapacity วัตถุประสงค์คือเพื่อให้แน่ใจว่าอาร์เรย์ไบต์ในสตรีมปัจจุบันสามารถรองรับการดำเนินการเขียนนี้ได้
วิธีนี้น่าสนใจมาก หากคุณพบว่า BUF ภายในไม่สามารถรองรับการดำเนินการเขียนนี้หลังจากการคำนวณวิธีการเติบโตจะถูกเรียกร้องให้มีการขยายตัว หลักการของการขยายกำลังการผลิตนั้นคล้ายคลึงกับของ ArrayList ขยายไปสู่ความจุดั้งเดิมสองเท่า
นอกจากนี้ ByteArrayOutputStream ยังมีวิธีการ writeto:
เป็นโมฆะที่ซิงโครไนซ์สาธารณะ writeto (outputstream ออก) โยน ioexception {out.write (buf, 0, count);}เขียนอาร์เรย์ไบต์ที่ห่อหุ้มภายในของเราลงในกระแสเอาต์พุต
วิธีที่เหลือบางอย่างก็ใช้กันทั่วไปเช่นกัน:
โปรดทราบว่าแม้ว่าสตรีมทั้งสองนี้จะเรียกว่า "สตรีม" แต่พวกเขาไม่ได้จัดสรรทรัพยากรบางอย่างเช่นสตรีมจริงดังนั้นเราไม่จำเป็นต้องเรียกวิธีการใกล้ชิดและมันก็ไร้ประโยชน์ที่จะเรียกมันว่า (เจ้าหน้าที่กล่าวว่าไม่มีผล)
กรณีทดสอบจะไม่ถูกปล่อยออกมา ฉันจะอัปโหลดกรณีรหัสทั้งหมดที่ใช้ในบทความนี้ในภายหลัง คุณสามารถเลือกที่จะดาวน์โหลดด้วยตัวเอง
เพื่อควบคุมความยาวการเรียนรู้ที่เหลือจะถูกวางไว้ในบทความถัดไป
รหัสรูปภาพและไฟล์ทั้งหมดในบทความจะถูกเก็บไว้ในคลาวด์บน GitHub ของฉัน:
(https://github.com/singleyam/overview_java)
คุณยังสามารถเลือกที่จะดาวน์โหลดในพื้นที่
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com