io 流主要提供四個接口java
InputStream
: 輸入字節流OutputStream
: 輸出字節流Reader
: 輸入字符流Writer
: 輸出字符流InputStream
輸入字節流,關注字節的讀取,io 包提供以下 InputStream 的實現git
ByteArrayInputStream
: 字節數組輸入流FileInputStream
: 文件字節輸入流PipedInputStream
: 管道輸入流,可和其餘的 PipedOutStream 鏈接,一般用於線程間通訊DataInputStream
: 二進制數據輸入流ObjectInputStream
: 對象輸入流BufferedInputStream
: 帶緩衝 buffer 的字節輸入流SequenceInputStream
: 能將多個字節流合併成一個PushbackInputStream
: 能回退的字節流InputStream
提供以下接口:github
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
輸出字節流,關注字節的寫入,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
字符輸入流,關注字符的讀取,io 包提供以下 Reader
的實現app
CharArrayReader
: 字符數組輸入流FileReader
: 文件字符輸入流PipedReader
: 管道輸入流,能夠和 PipedWriter
鏈接,一般用於線程間通訊StringReader
: 字符串輸入流BufferedReader
: 帶緩衝 buffer 的字符輸入流LineNumberReader
: 帶行號的字符輸入流PushbackReader
: 能回退的字符輸入流InputStreamReader
: 輸入字節流轉字符流Reader
提供以下接口:dom
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
字符輸出流,關注字符的寫入,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(); }
二進制字節流關注在基本數據類型的讀取和寫入,採用裝飾者模式,能裝飾在其餘流上
DataOutputStream
在 OutputStream
的基礎上新增了以下接口:
writeBoolean
: 寫入一個 boolean 值writeByte
: 寫入一個字節writeShort
: 寫入一個短整型writeInt
: 寫入一個整型writeLong
: 寫入一個長整型writeFloat
: 寫入一個浮點型writeDouble
: 寫入一個雙精度浮點型writeChar
: 寫入一個字符writeUTF
: 寫入一個 unicode 字符串DataInputStream
在 InputStream
的基礎上新增了以下接口:
readBoolean
: 讀取一個 boolean 值readByte
: 讀取一個字節readShort
: 讀取一個 shortreadInt
: 讀取一個整型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 的大小),容許將字節回退到緩衝區,若是超過了緩衝區大小,會拋出異常
PushbackInputStream
在 InputStream
的基礎上新增了以下接口:
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
將多個 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());
PipedInputStream
和 PipedOutputStream
經過調用 connect
方法創建鏈接,往 PipedOutputStream
寫入,能從 PipedInputStream
讀取,這種管道模式是一對一的,對一個管道流創建兩次鏈接會拋出異常
PipedOutputStream
在 OutputStream
的基礎上提供以下接口:
connect
: 與一個 PipedInputStream
創建鏈接,若是已經創建鏈接,將拋出異常PipedInputStream
在 InputStream
的基礎上提供以下接口:
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
提供了緩衝字符流的構造,能夠指定編碼
BufferedWriter
在 Writer
的基礎上,新增了以下接口:
newLine
: 寫入一個換行符BufferedReader
在 Reader
的基礎上,新增了以下接口:
readLine
: 讀取一個行,若是沒有新的行,返回 nulllines
: 返回一個 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(); }
InputStreamReader
和 OutputStreamWriter
能將字節流轉化字符流,還能夠指定編碼
{ 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
在 Reader
的基礎上,新增了以下接口:
setLineNumber
: 設置開始的文件行號,默認是 1getLineNumber
: 獲取當前的文件行號{ 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 的大小),容許將字符回退到緩衝區,若是超過了緩衝區大小,會拋出異常
PushbackReader
在 Reader
的基礎上新增了以下接口:
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()));
PipedReader
和 PipedWriter
經過調用 connect
方法創建鏈接,往 PipedWriter
寫入,能從 PipedReader
讀取,這種管道模式是一對一的,對一個管道流創建兩次鏈接會拋出異常
PipedWriter
在 Writer
的基礎上提供以下接口:
connect
: 與一個 PipedReader
創建鏈接,若是已經創建鏈接,將拋出異常PipedReader
在 Reader
的基礎上提供以下接口:
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(); }