1. กระแสข้อมูล

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

เราสามารถอ้างถึงพฤติกรรมทั้งหมดนี้ได้ในคำเดียว: การแลกเปลี่ยนข้อมูลระหว่างโปรแกรมกับโลกภายนอก รอนั่นไม่ใช่แค่คำเดียว

แน่นอนการแลกเปลี่ยนข้อมูลสามารถแบ่งออกเป็นสองส่วน: รับข้อมูลและส่งข้อมูล ตัวอย่างเช่น คุณอ่านข้อมูลจากแป้นพิมพ์โดยใช้Scannerวัตถุ — นี่คือการรับข้อมูล และคุณแสดงข้อมูลบนหน้าจอโดยใช้System.out.println()คำสั่ง — นี่คือการส่งข้อมูล

ในการเขียนโปรแกรม คำว่า "สตรีม" ใช้เพื่ออธิบายการแลกเปลี่ยนข้อมูล คำนั้นมาจากไหน?

ในชีวิตจริง คุณสามารถมีกระแสน้ำหรือกระแสแห่งสติได้ ในการ เขียนโปรแกรม เรามีdata streams

สตรีมเป็นเครื่องมืออเนกประสงค์ อนุญาตให้โปรแกรมรับข้อมูลจากทุกที่ (สตรีมอินพุต) และส่งข้อมูลได้ทุกที่ (สตรีมเอาต์พุต) ดังนั้นจึงมีสองประเภท:

  • กระแสอินพุตใช้สำหรับรับข้อมูล
  • กระแสข้อมูลออกสำหรับการส่งข้อมูล

ในการทำให้สตรีม ' จับต้องได้' ผู้สร้าง Java ได้เขียนสองคลาส: InputStreamและOutputStream

คลาสInputStreamมีread()เมธอดที่ให้คุณอ่านข้อมูลจากมันได้ และOutputStreamคลาสมีwrite()เมธอดที่ให้คุณเขียนข้อมูลลงไปได้ พวกเขามีวิธีอื่นเช่นกัน แต่จะเพิ่มเติมในภายหลัง

ไบต์สตรีม

เรากำลังพูดถึงข้อมูลประเภทใด ต้องใช้รูปแบบใด? กล่าวอีกนัยหนึ่ง คลาสเหล่านี้สนับสนุนประเภทข้อมูลใดบ้าง

คลาสเหล่านี้เป็นคลาสทั่วไป ดังนั้นจึงรองรับประเภทข้อมูลที่พบบ่อยที่สุด นั่นคือbyte. สามารถOutputStreamเขียนไบต์ (และอาร์เรย์ไบต์) และInputStreamวัตถุสามารถอ่านไบต์ (หรืออาร์เรย์ไบต์) แค่นั้นแหละ — ไม่รองรับประเภทข้อมูลอื่น ๆ

เป็นผลให้สตรีมเหล่านี้เรียกอีกอย่างว่าไบต์สตรี

คุณลักษณะหนึ่งของสตรีมคือข้อมูลสามารถอ่าน (หรือเขียน) ตามลำดับเท่านั้น คุณไม่สามารถอ่านข้อมูลจากตรงกลางของสตรีมโดยไม่อ่านข้อมูลทั้งหมดที่มาก่อน

นี่คือวิธีการอ่านข้อมูลจากแป้นพิมพ์ผ่านScannerชั้นเรียน: คุณอ่านข้อมูลจากแป้นพิมพ์ตามลำดับ ทีละบรรทัด เราอ่านบรรทัดหนึ่ง บรรทัดถัดไป บรรทัดถัดไป และอื่นๆ วิธีการอ่านบรรทัดถูกเรียกnextLine()ว่า

การเขียนข้อมูลไปOutputStreamยัง an จะเกิดขึ้นตามลำดับ ตัวอย่างที่ดีคือเอาต์พุตคอนโซล คุณส่งออกบรรทัดตามด้วยอีกอันหนึ่ง นี่คือเอาต์พุตตามลำดับ คุณไม่สามารถแสดงผลบรรทัดแรก บรรทัดที่สิบ และบรรทัดที่สอง ข้อมูลทั้งหมดถูกเขียนไปยังเอาต์พุตสตรีมตามลำดับเท่านั้น

สตรีมตัวละคร

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

จากข้อเท็จจริงนี้ โปรแกรมเมอร์ Java จึงเขียนคลาสเพิ่มเติมอีกสองคลาส: Readerและ Writerคลาส นั้นReaderคล้ายคลึงกับInputStreamคลาส แต่read()วิธีการอ่านไม่ใช่ไบต์ แต่เป็นอักขระ ( char) ชั้นWriterเรียนสอดคล้องกับOutputStreamชั้นเรียน และเช่นเดียวกับReaderคลาส ใช้ได้กับอักขระ ( char) ไม่ใช่ไบต์

หากเราเปรียบเทียบคลาสทั้งสี่นี้ เราจะได้ภาพต่อไปนี้:

ไบต์ (ไบต์) ตัวอักษร (ถ่าน)
การอ่านข้อมูล
InputStream
Reader
การเขียนข้อมูล
OutputStream
Writer

การใช้งานจริง

คลาสInputStream, OutputStream, ReaderและWriterตัวเองไม่ได้ใช้โดยตรงโดยใครก็ตาม เนื่องจากไม่เกี่ยวข้องกับออบเจกต์คอนกรีตใดๆ ที่สามารถอ่านข้อมูลได้ (หรือข้อมูลที่สามารถเขียนลงไปได้) แต่ทั้งสี่คลาสนี้ก็มีคลาสสืบสกุลที่สามารถทำอะไรได้มากมาย


2. InputStreamชั้นเรียน

คลาส นี้InputStreamน่าสนใจเพราะเป็นคลาสหลักสำหรับคลาสลูกหลานหลายร้อยคลาส ไม่มีข้อมูลของตัวเอง แต่มีเมธอดที่คลาสที่ได้รับมาทั้งหมดสืบทอดมา

โดยทั่วไป เป็นเรื่องยากสำหรับสตรีมอ็อบเจ็กต์ที่จะจัดเก็บข้อมูลไว้ภายใน สตรีมเป็นเครื่องมือสำหรับการอ่าน/เขียนข้อมูล แต่ไม่ใช่การจัดเก็บ ที่กล่าวว่ามีข้อยกเว้น

เมธอดของInputStreamคลาสและคลาสที่สืบทอดมาทั้งหมด:

วิธีการ คำอธิบาย
int read()
อ่านหนึ่งไบต์จากสตรีม
int read(byte[] buffer)
อ่านอาร์เรย์ของไบต์จากสตรีม
byte[] readAllBytes()
อ่านไบต์ทั้งหมดจากสตรีม
long skip(long n)
ข้ามnไบต์ในสตรีม (อ่านและละทิ้ง)
int available()
ตรวจสอบจำนวนไบต์ที่เหลืออยู่ในสตรีม
void close()
ปิดสตรีม

มาดูวิธีการเหล่านี้โดยสังเขป:

read()วิธี

เมธอดread()อ่านหนึ่งไบต์จากสตรีมและส่งกลับ คุณอาจสับสนกับintประเภทการคืนสินค้า เลือกประเภทนี้เนื่องจากintเป็นประเภทจำนวนเต็มมาตรฐาน สามไบต์แรกของค่าintจะเป็นศูนย์

read(byte[] buffer)วิธี

นี่คือตัวแปรที่สองของread()วิธีการ มันช่วยให้คุณอ่านอาร์เรย์ไบต์จากInputStreamทั้งหมดในครั้งเดียว อาร์เรย์ที่จะเก็บไบต์จะต้องผ่านเป็นอาร์กิวเมนต์ เมธอดส่งคืนตัวเลข — จำนวนไบต์ที่อ่านจริง

สมมติว่าคุณมีบัฟเฟอร์ 10 กิโลไบต์ และคุณกำลังอ่านข้อมูลจากไฟล์โดยใช้FileInputStreamคลาส หากไฟล์มีขนาดเพียง 2 กิโลไบต์ ข้อมูลทั้งหมดจะถูกโหลดลงในบัฟเฟอร์อาร์เรย์ และเมธอดจะคืนค่าเป็นตัวเลข 2048 (2 กิโลไบต์)

readAllBytes()วิธี

วิธีการที่ดีมาก มันแค่อ่านข้อมูลทั้งหมดจาก the InputStreamจนกว่าจะหมดและส่งกลับเป็นอาร์เรย์ไบต์เดียว สิ่งนี้มีประโยชน์มากสำหรับการอ่านไฟล์ขนาดเล็ก ไฟล์ขนาดใหญ่อาจไม่พอดีกับหน่วยความจำ และวิธีการจะส่งข้อยกเว้น

skip(long n)วิธี

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

ส่งกลับจำนวนไบต์ที่ถูกข้ามจริง (ในกรณีที่สตรีมสิ้นสุดก่อนที่nจะข้ามไบต์)

int available()วิธี

เมธอดส่งคืนจำนวนไบต์ที่ยังคงอยู่ในสตรีม

void close()วิธี

เมธอดclose()จะปิดสตรีมข้อมูลและปล่อยทรัพยากรภายนอกที่เกี่ยวข้อง เมื่อสตรีมถูกปิด จะไม่สามารถอ่านข้อมูลได้อีก

ลองเขียนโปรแกรมตัวอย่างที่คัดลอกไฟล์ขนาดใหญ่มาก เราไม่สามารถใช้readAllBytes()วิธีอ่านไฟล์ทั้งหมดลงในหน่วยความจำได้ ตัวอย่าง:

รหัส บันทึก
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreamสำหรับอ่านจากไฟล์
OutputStreamสำหรับเขียนลงไฟล์

บัฟเฟอร์ที่เราจะอ่านข้อมูล
ตราบเท่าที่มีข้อมูลในสตรีม

อ่านข้อมูลลงในบัฟเฟอร์
เขียนข้อมูลจากบัฟเฟอร์ไปยังสตรีมที่สอง

ในตัวอย่างนี้ เราใช้สองคลาส: FileInputStreamเป็นคลาสสืบทอดของInputStreamสำหรับการอ่านข้อมูลจากไฟล์ และFileOutputStreamเป็นคลาสสืบทอดของOutputStreamสำหรับการเขียนข้อมูลไปยังไฟล์ เราจะพูดถึงชั้นสองในภายหลัง

อีกจุดที่น่าสนใจที่นี่คือrealตัวแปร เมื่อบล็อกข้อมูลสุดท้ายถูกอ่านจากไฟล์ ข้อมูลนั้นอาจมีน้อยกว่า 64KB ได้อย่างง่ายดาย ดังนั้น เราไม่จำเป็นต้องส่งออกบัฟเฟอร์ทั้งหมด แต่เพียงบางส่วนเท่านั้น - realไบต์ แรก นี่คือสิ่งที่เกิดขึ้นในwrite()วิธีการ นี้



3. Readerชั้นเรียน

คลาส นี้Readerเป็นอะนาล็อกที่สมบูรณ์ของInputStreamคลาส ข้อแตกต่างเพียงอย่างเดียวคือใช้ได้กับอักขระ ( char) ไม่ใช่กับไบต์ เช่นเดียวกับInputStreamคลาส คลาสReaderจะไม่ถูกใช้โดยลำพัง: เป็นคลาสพาเรนต์สำหรับคลาสที่สืบทอดมาหลายร้อยคลาส และกำหนดเมธอดทั่วไปสำหรับคลาสเหล่านั้นทั้งหมด

วิธีการของReaderคลาส (และคลาสที่สืบทอดมาทั้งหมด):

วิธีการ คำอธิบาย
int read()
อ่านหนึ่งรายการcharจากสตรีม
int read(char[] buffer)
อ่านcharอาร์เรย์จากสตรีม
long skip(long n)
ข้ามไปn charsในสตรีม (อ่านแล้วทิ้งไป)
boolean ready()
ตรวจสอบว่ายังมีสิ่งที่เหลืออยู่ในสตรีมหรือไม่
void close()
ปิดสตรีม

เมธอดนั้นคล้ายกับของInputStreamคลาสมาก แม้ว่าจะมีความแตกต่างกันเล็กน้อยก็ตาม

int read()วิธี

เมธอดนี้อ่านหนึ่งรายการcharจากสตรีมและส่งกลับ ประเภทcharขยายเป็น an intแต่สองไบต์แรกของผลลัพธ์จะเป็นศูนย์เสมอ

int read(char[] buffer)วิธี

นี่คือตัวแปรที่สองของread()วิธีการ มันช่วยให้คุณอ่านอาร์เรย์ถ่านจากReaderทั้งหมดในครั้งเดียว อาร์เรย์ที่จะเก็บอักขระจะต้องผ่านเป็นอาร์กิวเมนต์ เมธอดส่งคืนตัวเลข — จำนวนอักขระที่อ่านจริง

skip(long n)วิธี

วิธีนี้ช่วยให้คุณข้ามอักขระ n ตัวแรกจากReaderวัตถุได้ มันทำงานเหมือนกับวิธีการแบบอะนาล็อกของInputStreamคลาส ทุกประการ ส่งกลับจำนวนอักขระที่ถูกข้ามจริง

boolean ready()วิธี

ส่งกลับtrueหากมีไบต์ที่ยังไม่ได้อ่านในสตรีม

void close()วิธี

เมธอดclose()จะปิดสตรีมข้อมูลและปล่อยทรัพยากรภายนอกที่เกี่ยวข้อง เมื่อสตรีมถูกปิด จะไม่สามารถอ่านข้อมูลได้อีก

สำหรับการเปรียบเทียบ ลองเขียนโปรแกรมที่คัดลอกไฟล์ข้อความ:

รหัส บันทึก
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerสำหรับอ่านจากไฟล์
Writerสำหรับเขียนลงไฟล์

บัฟเฟอร์ที่เราจะอ่านข้อมูล
ตราบเท่าที่มีข้อมูลในสตรีม

อ่านข้อมูลลงในบัฟเฟอร์
เขียนข้อมูลจากบัฟเฟอร์ไปยังสตรีมที่สอง