คลาสByteArrayOutputStreamใช้สตรีมเอาต์พุตที่เขียนข้อมูลไปยังอาร์เรย์ไบต์ บัฟเฟอร์จะเพิ่มขึ้นโดยอัตโนมัติเมื่อมีการเขียนข้อมูลลงไป
คลาสByteArrayOutputStreamสร้างบัฟเฟอร์ในหน่วยความจำ และข้อมูลทั้งหมดที่ส่งไปยังสตรีมจะถูกจัดเก็บไว้ในบัฟเฟอร์
ตัวสร้าง ByteArrayOutputStream
คลาสByteArrayOutputStreamมีตัวสร้างต่อไปนี้:
ตัวสร้าง | |
---|---|
ByteArrayOutputStream() | ตัวสร้างนี้สร้างบัฟเฟอร์ในหน่วยความจำที่มีความยาว 32 ไบต์ |
ByteArrayOutputStream(int a) | ตัวสร้างนี้สร้างบัฟเฟอร์ในหน่วยความจำที่มีขนาดเฉพาะ |
และนี่คือลักษณะของชั้นเรียนภายใน:
// The buffer itself, where the data is stored.
protected byte buf[];
// Current number of bytes written to the buffer.
protected int count;
public ByteArrayOutputStream() {
this(32);
}
public ByteArrayOutputStream(int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ size);
}
buf = new byte[size];
}
เมธอดของคลาส ByteArrayOutputStream
มาพูดคุยเกี่ยวกับวิธีการที่เราสามารถใช้ในชั้นเรียนของเรา
มาลองใส่อะไรบางอย่างในสตรีมของเรา ในการทำเช่นนี้ เราจะใช้ วิธี การเขียน () — สามารถรับหนึ่งไบต์หรือหนึ่งชุดของไบต์สำหรับการเขียน
วิธี | |
---|---|
เขียนเป็นโมฆะ (int b) | เขียนหนึ่งไบต์ |
เขียนเป็นโมฆะ (ไบต์ b [], int off, int len) | เขียนอาร์เรย์ของไบต์ที่มีขนาดเฉพาะ |
เป็นโมฆะ writeBytes (ไบต์ b[]) | เขียนอาร์เรย์ของไบต์ |
เป็นโมฆะ writeTo (OutputStream ออก) | เขียนข้อมูลทั้งหมดจากเอาต์พุตสตรีมปัจจุบันไปยังเอาต์พุตสตรีมที่ส่งผ่าน |
วิธีการดำเนินการ:
public static void main(String[] args) throws IOException {
ByteArrayOutputStream outputByte = new ByteArrayOutputStream();
// Write one byte
while(outputByte.size()!= 7) {
outputByte.write("codegym".getBytes());
}
// Write array of bytes
String value = "\nWelcome to Java\n";
byte[] arrBytes = value.getBytes();
outputByte.write(arrBytes);
// Write part of an array
String codeGym = "CodeGym";
byte[] b = codeGym.getBytes();
outputByte.write(b, 4, 3);
// Write to a file
FileOutputStream fileOutputStream = new FileOutputStream("output.txt");
outputByte.write(80);
outputByte.writeTo(fileOutputStream);
}
ผลลัพธ์คือ ไฟล์ output.txt ใหม่ ที่มีลักษณะดังนี้:
เมธอดtoByteArray()ส่งคืนเนื้อหาปัจจุบันของเอาต์พุตสตรีมนี้เป็นอาร์เรย์ของไบต์ และคุณสามารถใช้ เมธอด toString()เพื่อรับ อาร์เรย์ bufไบต์เป็นข้อความ:
public static void main(String[] args) throws IOException {
ByteArrayOutputStream outputByte = new ByteArrayOutputStream();
String value = "CodeGym";
outputByte.write(value.getBytes());
byte[] result = outputByte.toByteArray();
System.out.println("Result: ");
for(int i = 0 ; i < result.length; i++) {
// Display the characters
System.out.print((char)result[i]);
}
}
บัฟเฟอร์ของเรามีอาร์เรย์ไบต์ที่เราส่งไป
วิธี การรีเซ็ต ()จะรีเซ็ตจำนวนไบต์ที่ถูกต้องในสตรีมเอาต์พุตอาร์เรย์ไบต์ให้เป็นศูนย์ (ดังนั้นทุกอย่างที่สะสมในเอาต์พุตจะถูกรีเซ็ต)
public static void main(String[] args) throws IOException {
ByteArrayOutputStream outputByte = new ByteArrayOutputStream(120);
String value = "CodeGym";
outputByte.write(value.getBytes());
byte[] result = outputByte.toByteArray();
System.out.println("Output before reset: ");
for (byte b : result) {
// Display the characters
System.out.print((char) b);
}
outputByte.reset();
byte[] resultAfterReset = outputByte.toByteArray();
System.out.println("\nOutput after reset: ");
for (byte b : resultAfterReset) {
// Display the characters
System.out.print((char) b);
}
}
เมื่อเราแสดงบัฟเฟอร์หลังจากเรียกใช้ เมธอด reset()เราจะไม่ได้รับอะไรเลย
คุณสมบัติเฉพาะของเมธอด close()
วิธีนี้สมควรได้รับความสนใจเป็นพิเศษ เพื่อให้เข้าใจว่ามันทำอะไร ลองมาดูข้างในกัน:
/**
* Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*/
public void close() throws IOException {
}
โปรดทราบว่า เมธอด close()ของคลาสByteArrayOutputStreamไม่ได้ทำอะไรเลย
ทำไมถึงเป็นเช่นนั้น? ByteArrayOutputStream เป็นสตรีมที่ใช้หน่วยความจำ (กล่าวคือ จัดการและเติมข้อมูลโดยผู้ใช้ในโค้ด) ดังนั้นการเรียกclose() จึงไม่มีผลใดๆ
ฝึกฝน
ตอนนี้ มาลองใช้ระบบไฟล์โดยใช้ByteArrayOutputStream และ ByteArrayInputStream ของ เรา
มาเขียน คลาส FileSystemโดยใช้ รูปแบบการออกแบบ singletonและใช้ Static HashMap<String, byte[]>โดยที่:
- สตริงคือเส้นทางไปยังไฟล์
- byte[]คือข้อมูลในไฟล์ที่บันทึก
import java.io.*;
import java.util.HashMap;
import java.util.Map;
class FileSystem {
private static final FileSystem fileSystem = new FileSystem();
private static final Map<String, byte[]> files = new HashMap<>();
private FileSystem() {
}
public static FileSystem getFileSystem() {
return fileSystem;
}
public void create(String path) {
validateNotExists(path);
files.put(path, new byte[0]);
}
public void delete(String path) {
validateExists(path);
files.remove(path);
}
public boolean isExists(String path) {
return files.containsKey(path);
}
public InputStream newInputStream(String path) {
validateExists(path);
return new ByteArrayInputStream(files.get(path));
}
public OutputStream newOutputStream(String path) {
validateExists(path);
return new ByteArrayOutputStream() {
@Override
public void flush() throws IOException {
final byte[] bytes = toByteArray();
files.put(path, bytes);
super.flush();
}
@Override
public void close() throws IOException {
final byte[] bytes = toByteArray();
files.put(path, bytes);
super.close();
}
};
}
private void validateExists(String path) {
if (!files.containsKey(path)) {
throw new RuntimeException("File not found");
}
}
private void validateNotExists(String path) {
if (files.containsKey(path)) {
throw new RuntimeException("File exists");
}
}
}
ในคลาสนี้ เราสร้างวิธีการสาธารณะต่อไปนี้:
- วิธี CRUD มาตรฐาน (สร้าง อ่าน อัปเดต ลบ)
- วิธีการตรวจสอบว่ามีไฟล์อยู่หรือไม่
- วิธีการรับอินสแตนซ์ของระบบไฟล์
หากต้องการอ่าน จากไฟล์ เราจะส่งคืนInputStream ภายใต้ประทุนคือการใช้งานByteArrayInputStream บัฟเฟอร์คืออาร์เรย์ไบต์ที่จัดเก็บไว้ในแผนที่ไฟล์
อีกวิธีที่น่าสนใจคือnewOutputStream เมื่อเมธอดนี้ถูกเรียกใช้ เราจะส่งคืนออบเจกต์ ByteArrayOutputStream ใหม่ที่แทนที่เมธอดสองเมธอด: flushและclose การเรียกใช้เมธอดเหล่านี้ควรทำให้การเขียนเกิดขึ้น
และนั่นคือสิ่งที่เราทำ: เราได้รับอาร์เรย์ไบต์ที่ผู้ใช้เขียนถึง และจัดเก็บสำเนาเป็นค่าใน การแม็พ ไฟล์ด้วยคีย์ที่เหมาะสม
เราใช้รหัสต่อไปนี้เพื่อทดสอบระบบไฟล์ของเรา (FS):
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static java.nio.charset.StandardCharsets.UTF_8;
public class MyFileSystemTest {
public static void main(String[] args) throws IOException {
FileSystem fileSystem = FileSystem.getFileSystem();
final String path = "/user/bin/data.txt";
// Create a file
fileSystem.create(path);
System.out.println("File created successfully");
// Make sure it's empty
try (InputStream inputStream = fileSystem.newInputStream(path)) {
System.out.print("File contents:\t");
System.out.println(read(inputStream));
}
// Write data to it
try (final OutputStream outputStream = fileSystem.newOutputStream(path)) {
outputStream.write("CodeGym".getBytes(UTF_8));
System.out.println("Data written to file");
}
// Read data
try (InputStream inputStream = fileSystem.newInputStream(path)) {
System.out.print("File contents:\t");
System.out.println(read(inputStream));
}
// Delete the file
fileSystem.delete(path);
// Verify that the file does not exist in the FS
System.out.print("File exists:\t");
System.out.println(fileSystem.isExists(path));
}
private static String read(InputStream inputStream) throws IOException {
return new String(inputStream.readAllBytes(), UTF_8);
}
}
ในระหว่างการทดสอบ เราตรวจสอบการดำเนินการต่อไปนี้:
- เราสร้างไฟล์ใหม่
- เราตรวจสอบว่าไฟล์ที่สร้างขึ้นนั้นว่างเปล่า
- เราเขียนข้อมูลบางอย่างลงในไฟล์
- เราอ่านข้อมูลย้อนหลังและตรวจสอบว่าตรงกับที่เราเขียนไว้หรือไม่
- เราลบไฟล์.
- เราตรวจสอบว่าไฟล์ถูกลบแล้ว
การรันโค้ดนี้ให้ผลลัพธ์นี้แก่เรา:
เนื้อหาไฟล์:
ข้อมูลที่เขียนไปยังไฟล์
เนื้อหาไฟล์:
มีไฟล์ CodeGym: เท็จ
ทำไมตัวอย่างนี้ถึงจำเป็น?
พูดง่ายๆ ก็คือ ข้อมูลจะเป็นชุดของไบต์เสมอ หากคุณต้องการอ่าน/เขียนข้อมูลจำนวนมากจาก/ไปยังดิสก์ โค้ดของคุณจะทำงานช้าเนื่องจากปัญหา I/O ในกรณีนี้ การรักษาระบบไฟล์เสมือนใน RAM เป็นเรื่องที่สมเหตุสมผล โดยทำงานร่วมกับระบบในลักษณะเดียวกับที่คุณทำกับดิสก์แบบเดิม และอะไรจะง่ายไปกว่าInputStreamและOutputStream ?
แน่นอนว่านี่เป็นตัวอย่างสำหรับคำแนะนำ ไม่ใช่โค้ดที่พร้อมสำหรับการผลิต ไม่นับ (รายการต่อไปนี้ไม่ครอบคลุม):
- มัลติเธรด
- ขีดจำกัดขนาดไฟล์ (จำนวน RAM ที่มีอยู่สำหรับ JVM ที่รันอยู่)
- การตรวจสอบโครงสร้างเส้นทาง
- การตรวจสอบอาร์กิวเมนต์เมธอด
ข้อมูลวงในที่น่าสนใจ:
เซิร์ฟเวอร์การตรวจสอบงาน CodeGym ใช้วิธีการที่คล้ายกัน เราสร้าง FS เสมือน กำหนดว่าการทดสอบใดจำเป็นต้องเรียกใช้สำหรับการตรวจสอบงาน เรียกใช้การทดสอบ และอ่านผลลัพธ์
บทสรุปและคำถามใหญ่
คำถามใหญ่หลังจากอ่านบทเรียนนี้คือ "ทำไมฉันถึงใช้ byte[]ไม่ได้เนื่องจากสะดวกกว่าและไม่มีข้อจำกัด"
ข้อได้เปรียบของByteArrayInputStreamคือมันบ่งชี้ว่าคุณกำลังจะใช้ไบต์แบบอ่านอย่างเดียว (เนื่องจากสตรีมไม่มีอินเทอร์เฟซสำหรับเปลี่ยนเนื้อหา) ที่กล่าวว่าเป็นสิ่งสำคัญที่จะต้องทราบว่าโปรแกรมเมอร์ ยังคง สามารถเข้าถึงไบต์ได้โดยตรง
แต่ถ้าบางครั้งคุณมีbyte[]บางครั้งคุณมีไฟล์ บางครั้งคุณมีการเชื่อมต่อเครือข่าย และอื่นๆ คุณจะต้องมีสิ่งที่เป็นนามธรรมสำหรับ "a stream of bytes และฉันไม่สนใจว่าพวกมันจะอยู่ที่ไหน มาจาก". และนั่นคือสิ่งที่InputStreamเป็น เมื่อแหล่งที่มาเป็นอาร์เรย์ไบต์ByteArrayInputStreamจะเป็นInputStream ที่ดี ที่จะใช้
สิ่งนี้มีประโยชน์ในหลาย ๆ สถานการณ์ แต่นี่คือตัวอย่างเฉพาะสองตัวอย่าง:
-
คุณกำลังเขียนไลบรารีที่รับไบต์และประมวลผลด้วยวิธีใดวิธีหนึ่ง (ตัวอย่างเช่น สมมติว่าเป็นไลบรารีของโปรแกรมอรรถประโยชน์การประมวลผลภาพ) ผู้ใช้ไลบรารีของคุณสามารถระบุไบต์จากไฟล์ จากไบต์ในหน่วยความจำ[]หรือจากแหล่งอื่น ดังนั้นคุณจึงจัดเตรียมอินเทอร์เฟ ซที่ยอมรับInputStreamซึ่งหมายความว่าหากมีbyte[]พวกเขาจำเป็นต้องรวมไว้ในByteArrayInputStream
-
คุณกำลังเขียนโค้ดที่อ่านการเชื่อมต่อเครือข่าย แต่ในการทำการทดสอบหน่วยกับรหัสนี้ คุณไม่ต้องการเปิดการเชื่อมต่อ — คุณเพียงแค่ต้องการป้อนโค้ดไม่กี่ไบต์ ดังนั้นโค้ดจะใช้InputStream และการ ทดสอบของคุณผ่านในByteArrayInputStream
GO TO FULL VERSION