java io 流

java io 流

io 流總覽

在這裏插入圖片描述

io 流主要提供四個接口

  • InputStream: 輸入字節流
  • OutputStream: 輸出字節流
  • Reader: 輸入字符流
  • Writer: 輸出字符流

InputStream

InputStream 輸入字節流,關注字節的讀取,io 包提供如下 InputStream 的實現

  • ByteArrayInputStream: 字節數組輸入流
  • FileInputStream: 文件字節輸入流
  • PipedInputStream: 管道輸入流,可和其他的 PipedOutStream 連接,通常用於線程間通信
  • DataInputStream: 二進制數據輸入流
  • ObjectInputStream: 對象輸入流
  • BufferedInputStream: 帶緩衝 buffer 的字節輸入流
  • SequenceInputStream: 能將多個字節流合併成一個
  • PushbackInputStream: 能回退的字節流

InputStream 提供如下接口:

  • read: 從流中讀取一個字節
  • read(buffer): 從流中讀取字節到 buffer 中,返回真實讀取的字節數
  • read(buffer, offset, length): 從流中讀取 length 個字節,寫入到 buffer 的 offset 處,返回真實讀取的字節數
  • readNBytes(buffer, offset, length): 和 read 一樣,但是保證讀取 length 個字節,除非流中沒有數據
  • readAllBytes: 讀取所有字節,返回一個字節數組
  • skip: 跳過前 n 個字節
  • available: 剩餘字節數
  • mark: 標記當前讀取的位置
  • reset: 將流指針重置到上次標記的位置
  • close: 關閉流,釋放資源
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    assertEquals(in.read(), '0');
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    byte[] buf = new byte[4];
    assertEquals(in.read(buf), 4);
    assertArrayEquals(buf, "0123".getBytes());
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    byte[] buf = new byte[20];
    assertEquals(in.read(buf), 10);
    assertArrayEquals(Arrays.copyOf(buf, 10), "0123456789".getBytes());
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    byte[] buf = new byte[20];
    assertEquals(in.read(buf, 1, 4), 4);
    assertArrayEquals(Arrays.copyOfRange(buf, 1, 1 + 4), "0123".getBytes());
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    byte[] buf = new byte[20];
    assertEquals(in.readNBytes(buf, 1, 4), 4);
    assertArrayEquals(Arrays.copyOfRange(buf, 1, 1 + 4), "0123".getBytes());
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    assertArrayEquals(in.readAllBytes(), "0123456789".getBytes());
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    assertEquals(in.skip(2), 2);
    assertEquals(in.available(), 8);
    assertEquals(in.read(), '2');
    assertEquals(in.available(), 7);
    in.mark(0);
    assertEquals(in.read(), '3');
    in.reset();
    assertEquals(in.available(), 7);
    assertEquals(in.read(), '3');
    in.close();
}
{
    InputStream in = new ByteArrayInputStream("0123456789".getBytes());
    for (int ch = in.read(); ch != -1; ch = in.read()) {
        System.out.println(ch);
    }
}

OutputStream

OutputStream 輸出字節流,關注字節的寫入,io 包提供瞭如下 OutputStream 的實現

  • ByteArrayOutputStream: 輸出 byte 數組
  • FileOutputStream: 文件輸出流
  • PipedOutputStream: 管道輸出流,可和其他的 PipedInputStream 連接,通常用於線程間通信
  • DataOutputStream: 二進制數據輸出流
  • ObjectOutputStream: 對象輸出流
  • BufferedOutputStream: 帶緩衝 buffer 的輸出流
  • SequenceOutputStream: 能將多個輸出流合併成一個

OutputStream 提供如下接口:

  • write: 寫入一個字節
  • write(buffer): 寫入 buffer 中的數據
  • write(buffer, offset, length): 寫入 buffer 從 offset 起的 length 個字節的數據
  • flush: 將緩衝區的數據刷到實際的存儲中
  • close: 關閉流
OutputStream out = new ByteArrayOutputStream();
out.write('0');
out.write("123456789".getBytes());
out.write("0123456789".getBytes(), 1, 2);
out.flush();
out.close();

Reader

Reader 字符輸入流,關注字符的讀取,io 包提供如下 Reader 的實現

  • CharArrayReader: 字符數組輸入流
  • FileReader: 文件字符輸入流
  • PipedReader: 管道輸入流,可以和 PipedWriter 連接,通常用於線程間通信
  • StringReader: 字符串輸入流
  • BufferedReader: 帶緩衝 buffer 的字符輸入流
  • LineNumberReader: 帶行號的字符輸入流
  • PushbackReader: 能回退的字符輸入流
  • InputStreamReader: 輸入字節流轉字符流

Reader 提供如下接口:

  • read: 從流中讀取一個字符
  • read(buffer): 從流中讀取字符到 buffer 中,返回真實讀取的字符數
  • read(buffer, offset, length): 從流中讀取 length 個字符,寫入到 buffer 的 offset 處,返回真實讀取的字符數
  • read(CharBuffer: 從流中讀取字符到 CharBuffer 中,返回真實讀取的字符數
  • skip: 跳過前 n 個字符
  • mark: 標記當前讀取的位置
  • reset: 將流指針重置到上次標記的位置
  • close: 關閉流,釋放資源
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    assertEquals(reader.read(), '0');
}
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    char[] buf = new char[4];
    assertEquals(reader.read(buf), 4);
    assertArrayEquals(buf, "0123".toCharArray());
}
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    char[] buf = new char[20];
    assertEquals(reader.read(buf), 10);
    assertArrayEquals(Arrays.copyOf(buf, 10), "0123456789".toCharArray());
}
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    char[] buf = new char[20];
    assertEquals(reader.read(buf, 1, 4), 4);
    assertArrayEquals(Arrays.copyOfRange(buf, 1, 1 + 4), "0123".toCharArray());
}
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    CharBuffer buf = CharBuffer.allocate(20);
    assertEquals(reader.read(buf), 10);
}
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    assertTrue(reader.ready());
    assertEquals(reader.skip(2), 2);
    assertEquals(reader.read(), '2');
    reader.mark(0);
    assertEquals(reader.read(), '3');
    reader.reset();
    assertEquals(reader.read(), '3');
    reader.close();
}
{
    Reader reader = new CharArrayReader("0123456789".toCharArray());
    for (int ch = reader.read(); ch != -1; ch = reader.read()) {
        System.out.println(ch);
    }
}

Writer

Writer 字符輸出流,關注字符的寫入,io 包提供如下 Writer 的實現

  • CharArrayWriter: 字符數組輸出流
  • FileWriter: 文件字符輸出流
  • PipedWriter: 管道輸出流,可以和 PipedReader 連接,通常用於線程間通信
  • StringWriter: 字符串輸出流
  • BufferedWriter: 帶緩衝 buffer 的字符輸出流
  • OutputStreamWriter: 輸出字節流轉字符流

Writer 提供如下接口:

  • write(char): 寫入一個字符
  • write(string): 寫入一個字符串
  • write(string, offset, length): 寫入 string 從 offset 起的 length 個字符的數據
  • write(char[]): 寫入字符數組中的數據
  • write(char[], offset, length): 寫入字符數組從 offset 起的 length 個字符的數據
  • append(ch): 寫入一個字符,和 write 一樣
  • append(CharSequence): 寫入字符序列的所有數據(String, StringBuilder, StringBuffer 都是 CharSequence 的子類)
  • append(CharSequence, offset, length): 寫入字符序列從 offset 起的 length 個字符的數據
  • flush: 將緩衝區的數據刷到實際的存儲中
  • close: 關閉流
Writer writer = new CharArrayWriter();
writer.write('0');
writer.write("0123456789");
writer.write("0123456789", 1, 4);
writer.write("0123456789".toCharArray());
writer.write("0123456789".toCharArray(), 1, 4);
writer.append('0');
writer.append(new StringBuilder("0123456789"));
writer.append(new StringBuilder("0123456789"), 1, 4);
writer.flush();
writer.close();

文件字節流

文件字節流關注文件的讀取和寫入

{
    FileOutputStream fout = new FileOutputStream("/tmp/test.txt");
    fout.write("No patient who, who has no wisdom".getBytes());
    fout.close();
}
{
    FileInputStream fin = new FileInputStream("/tmp/test.txt");
    assertArrayEquals(fin.readAllBytes(), "No patient who, who has no wisdom".getBytes());
    fin.close();
}

緩衝字節流

緩衝字節流採用裝飾者模式,裝飾在其他流上,使流擁有了緩存功能,從而提高讀寫了效率

{
    BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream("/tmp/test.txt"));
    bout.write("People lack the willpower, rather than strength".getBytes());
    bout.close();
}
{
    BufferedInputStream bin = new BufferedInputStream(new FileInputStream("/tmp/test.txt"));
    assertArrayEquals(bin.readAllBytes(), "People lack the willpower, rather than strength".getBytes());
    bin.close();
}

二進制字節流

二進制字節流關注在基本數據類型的讀取和寫入,採用裝飾者模式,能裝飾在其他流上

DataOutputStreamOutputStream 的基礎上新增瞭如下接口:

  • writeBoolean: 寫入一個 boolean 值
  • writeByte: 寫入一個字節
  • writeShort: 寫入一個短整型
  • writeInt: 寫入一個整型
  • writeLong: 寫入一個長整型
  • writeFloat: 寫入一個浮點型
  • writeDouble: 寫入一個雙精度浮點型
  • writeChar: 寫入一個字符
  • writeUTF: 寫入一個 unicode 字符串

DataInputStreamInputStream 的基礎上新增瞭如下接口:

  • readBoolean: 讀取一個 boolean 值
  • readByte: 讀取一個字節
  • readShort: 讀取一個 short
  • readInt: 讀取一個整型
  • readLong: 讀取一個長整型
  • readFloat: 讀取一個浮點型
  • readDouble: 讀取一個雙精度浮點型
  • readChar: 讀取一個字符
  • readUTF: 讀取一個 unicode 字符串
{
    DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("/tmp/test.txt")));
    dout.writeBoolean(false);
    dout.writeByte('x');
    dout.writeShort(123);
    dout.writeInt(123456);
    dout.writeLong(123456789);
    dout.writeFloat((float) 123.456);
    dout.writeDouble(123.456);
    dout.writeUTF("Rome wasn’t built in one day");
    dout.close();
}
{
    DataInputStream din = new DataInputStream(new BufferedInputStream(new FileInputStream("/tmp/test.txt")));
    assertEquals(din.readBoolean(), false);
    assertEquals(din.readByte(), 'x');
    assertEquals(din.readShort(), 123);
    assertEquals(din.readInt(), 123456);
    assertEquals(din.readLong(), 123456789);
    assertEquals(din.readFloat(), (float) 123.456);
    assertEquals(din.readDouble(), 123.456);
    assertEquals(din.readUTF(), "Rome wasn’t built in one day");
    din.close();
}

對象字節流

對象字節流關注對象的寫入和讀取,同時擁有二進制字節流的所有功能,同樣採用裝飾者模式

ObjectOutputStream 相比 DataOutputStream 新增瞭如下接口:

  • writeObject: 寫入任何 Serializable 對象

ObjectInputStream 相比 DataInputStream 新增瞭如下接口:

  • readObject: 從流中讀取一個對象
{
    ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("/tmp/test.txt")));
    oout.writeBoolean(false);
    oout.writeByte('x');
    oout.writeShort(123);
    oout.writeInt(123456);
    oout.writeLong(123456789);
    oout.writeFloat((float) 123.456);
    oout.writeDouble(123.456);
    oout.writeUTF("Nothing is impossible to a willing heart");
    oout.writeObject(new Point(123, 456));
    oout.close();
}
{
    ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream("/tmp/test.txt")));
    assertEquals(oin.readBoolean(), false);
    assertEquals(oin.readByte(), 'x');
    assertEquals(oin.readShort(), 123);
    assertEquals(oin.readInt(), 123456);
    assertEquals(oin.readLong(), 123456789);
    assertEquals(oin.readFloat(), (float) 123.456);
    assertEquals(oin.readDouble(), 123.456);
    assertEquals(oin.readUTF(), "Nothing is impossible to a willing heart");
    Point point = (Point) oin.readObject();
    assertEquals(point.x, 123);
    assertEquals(point.y, 456);
    oin.close();
}

回退字節流

可回退字節流內部維護了一個固定大小的緩衝區(可通過構造函數配置 buffer 的大小),允許將字節回退到緩衝區,如果超過了緩衝區大小,會拋出異常

PushbackInputStreamInputStream 的基礎上新增瞭如下接口:

  • unread: 回退一個字節
  • unread(buffer): 將 buffer 中的數據回退到流的緩衝區
  • unread(buffer, offset, length): 從 buffer 的 offset 處回退 length 個字節到流緩衝區
PushbackInputStream pin = new PushbackInputStream(new ByteArrayInputStream("Failure is the mother of success".getBytes()), 10);
byte[] buf = new byte[7];
assertEquals(pin.read(buf), 7);
assertArrayEquals(buf, "Failure".getBytes());
pin.unread(buf);
assertEquals(pin.read(buf), 7);
assertArrayEquals(buf, "Failure".getBytes());
// 超過 buffer 的大小,拋出 IOException
assertThrows(IOException.class, () -> pin.unread("01234567890".getBytes()));

SequenceInputStream

SequenceInputStream 將多個 InputStream 合併成一個

InputStream in1 = new ByteArrayInputStream("For man is man and master of his fate\n".getBytes());
InputStream in2 = new ByteArrayInputStream("Cease to struggle and you cease to live\n".getBytes());
Vector<InputStream> vi = new Vector<>(List.of(in1, in2));
SequenceInputStream sin = new SequenceInputStream(vi.elements());
assertArrayEquals(sin.readAllBytes(), "For man is man and master of his fate\nCease to struggle and you cease to live\n".getBytes());

管道字節流

PipedInputStreamPipedOutputStream 通過調用 connect 方法建立連接,往 PipedOutputStream 寫入,能從 PipedInputStream 讀取,這種管道模式是一對一的,對一個管道流建立兩次連接會拋出異常

PipedOutputStreamOutputStream 的基礎上提供如下接口:

  • connect: 與一個 PipedInputStream 建立連接,如果已經建立連接,將拋出異常

PipedInputStreamInputStream 的基礎上提供如下接口:

  • connect: 與一個 PipedOutputStream 建立連接,如果已經建立連接,將拋出異常
ExecutorService es = Executors.newCachedThreadPool();
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream();
pin.connect(pout);

es.execute(() -> {
    try {
        ObjectOutputStream oout = new ObjectOutputStream(pout);
        oout.writeInt(123456);
        oout.writeUTF("如果你還沒能找到讓自己熱愛的事業,繼續尋找,不要放棄");
        oout.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
});

es.execute(() -> {
    try {
        ObjectInputStream oin = new ObjectInputStream(pin);
        assertEquals(oin.readInt(), 123456);
        assertEquals(oin.readUTF(), "如果你還沒能找到讓自己熱愛的事業,繼續尋找,不要放棄");
        oin.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
});

try {
    es.shutdown();
    while (!es.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
        // nothing to do
    }
} catch (Exception e) {
    e.printStackTrace();
}

文件字符流

文件字符流關注文件的讀取和寫入,使用默認的 utf-8 來編碼

{
    FileWriter fw = new FileWriter("/tmp/test.txt");
    assertEquals(fw.getEncoding(), "UTF8");
    System.out.println(fw.getEncoding());
    fw.write("初學者的心態;擁有初學者的心態是件了不起的事情");
    fw.flush();
    fw.close();
}
{
    FileReader fr = new FileReader("/tmp/test.txt");
    assertEquals(fr.getEncoding(), "UTF8");
    StringBuilder sb = new StringBuilder();
    for (int ch = fr.read(); ch != -1; ch = fr.read()) {
        sb.append((char) ch);
    }
    assertEquals(sb.toString(), "初學者的心態;擁有初學者的心態是件了不起的事情");
    fr.close();
}

緩衝字符流

採用裝飾者模式,裝飾在其他字符流上,增加緩存功能,提高讀寫性能。Files 提供了緩衝字符流的構造,可以指定編碼

BufferedWriterWriter 的基礎上,新增瞭如下接口:

  • newLine: 寫入一個換行符

BufferedReaderReader 的基礎上,新增瞭如下接口:

  • readLine: 讀取一個行,如果沒有新的行,返回 null
  • lines: 返回一個 java.util.stream.Stream,支持 java 8 的流式處理
{
    // BufferedWriter bw = new BufferedWriter(new FileWriter("/tmp/test.txt"));
    BufferedWriter bw = Files.newBufferedWriter(Paths.get("/tmp/test.txt"), Charsets.UTF_8);
    bw.write("窮則獨善其身,達則兼濟天下");
    bw.newLine();
    bw.write("玉不琢、不成器,人不學、不知義");
    bw.newLine();
    bw.close();
}
{
    // BufferedReader br = new BufferedReader(new FileReader("/tmp/test.txt"));
    BufferedReader br = Files.newBufferedReader(Paths.get("/tmp/test.txt"), Charsets.UTF_8);
    assertEquals(br.readLine(), "窮則獨善其身,達則兼濟天下");
    assertEquals(br.readLine(), "玉不琢、不成器,人不學、不知義");
    assertEquals(br.readLine(), null);
    br.close();
}
{
    // BufferedReader br = new BufferedReader(new FileReader("/tmp/test.txt"));
    BufferedReader br = Files.newBufferedReader(Paths.get("/tmp/test.txt"), Charsets.UTF_8);
    assertThat(br.lines().collect(Collectors.toList()), equalTo(List.of(
            "窮則獨善其身,達則兼濟天下",
            "玉不琢、不成器,人不學、不知義"
    )));
    br.close();
}

StreamReaderWriter

InputStreamReaderOutputStreamWriter 能將字節流轉化字符流,還可以指定編碼

{
    OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("/tmp/test.txt"), "utf-8");
    ow.write("你究竟是想一輩子賣糖水,還是希望獲得改變世界的機遇");
    ow.flush();
    ow.close();
}
{
    InputStreamReader rw = new InputStreamReader(new FileInputStream("/tmp/test.txt"), "utf-8");
    StringBuilder sb = new StringBuilder();
    for (int ch = rw.read(); ch != -1; ch = rw.read()) {
        sb.append((char) ch);
    }
    assertEquals(sb.toString(), "你究竟是想一輩子賣糖水,還是希望獲得改變世界的機遇");
    rw.close();
}

字符串流

字符串構建的流

{
    StringWriter sw = new StringWriter();
    sw.write("學而不思則罔,思而不學則殆");
    assertEquals(sw.getBuffer().toString(), "學而不思則罔,思而不學則殆");
    sw.close();
}
{
    StringReader sr = new StringReader("一年之計在於春,一日之計在於晨");
    StringBuilder sb = new StringBuilder();
    for (int ch = sr.read(); ch != -1; ch = sr.read()) {
        sb.append((char) ch);
    }
    assertEquals(sb.toString(), "一年之計在於春,一日之計在於晨");
}

LineNumberReader

LineNumberReader 支持行號的字符流

LineNumberReaderReader 的基礎上,新增瞭如下接口:

  • setLineNumber: 設置開始的文件行號,默認是 1
  • getLineNumber: 獲取當前的文件行號
{
    BufferedWriter bw = new BufferedWriter(new FileWriter("/tmp/test.txt"));
    bw.write("富貴不能淫\n貧賤不能移\n威武不能屈\n此之謂大丈夫\n");
    bw.close();
}
{
    LineNumberReader lr = new LineNumberReader(new BufferedReader(new FileReader("/tmp/test.txt")));
    List<String> lines = new LinkedList<>();
    for (String line = lr.readLine(); line != null; line = lr.readLine()) {
        lines.add(lr.getLineNumber() + " " + line);
    }
    assertThat(lines, equalTo(List.of(
            "1 富貴不能淫", "2 貧賤不能移", "3 威武不能屈", "4 此之謂大丈夫"
    )));
}

回退字符流

可回退字符流內部維護了一個固定大小的緩衝區(可通過構造函數配置 buffer 的大小),允許將字符回退到緩衝區,如果超過了緩衝區大小,會拋出異常

PushbackReaderReader 的基礎上新增瞭如下接口:

  • unread: 回退一個字符
  • unread(cbar[]): 將 buffer 中的數據回退到流的緩衝區
  • unread(char[], offset, length): 從 buffer 的 offset 處回退 length 個字節到流緩衝區
PushbackReader pr = new PushbackReader(new StringReader("蚍蜉撼大樹,可笑不自量"), 10);
char[] buf = new char[5];
assertEquals(pr.read(buf), 5);
assertArrayEquals(buf, "蚍蜉撼大樹".toCharArray());
pr.unread(buf);
assertEquals(pr.read(buf), 5);
assertArrayEquals(buf, "蚍蜉撼大樹".toCharArray());
// 超過 buffer 的大小,拋出 IOException
assertThrows(IOException.class, () -> pr.unread("01234567890".toCharArray()));

管道字符流

PipedReaderPipedWriter 通過調用 connect 方法建立連接,往 PipedWriter 寫入,能從 PipedReader 讀取,這種管道模式是一對一的,對一個管道流建立兩次連接會拋出異常

PipedWriterWriter 的基礎上提供如下接口:

  • connect: 與一個 PipedReader 建立連接,如果已經建立連接,將拋出異常

PipedReaderReader 的基礎上提供如下接口:

  • connect: 與一個 PipedWriter 建立連接,如果已經建立連接,將拋出異常
ExecutorService es = Executors.newCachedThreadPool();
PipedReader pr = new PipedReader();
PipedWriter pw = new PipedWriter();
pr.connect(pw);

es.execute(() -> {
    try {
        BufferedWriter bw = new BufferedWriter(pw);
        bw.write("活着就是爲了改變世界,難道還有其他原因嗎");
        bw.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
});

es.execute(() -> {
    try {
        BufferedReader br = new BufferedReader(pr);
        assertEquals(br.readLine(), "活着就是爲了改變世界,難道還有其他原因嗎");
        br.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
});

try {
    es.shutdown();
    while (!es.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
        // nothing to do
    }
} catch (Exception e) {
    e.printStackTrace();
}

鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章