IO流
1 流的作用:
流是對各類文件的操作,而且是從頭到尾進行讀和寫的 (按順序) 不想RandomAccessFile 一樣可以隨意
2 流的分類
按照方向:輸入輸出流
* a: 參照物
*到底是輸入還是輸出,都是以Java程序爲參照
*b: Output
*把內存中的數據存儲到持久化設備上這個動作稱爲輸出(寫)Output操作
*程序到文件稱爲輸出
*c: Input
*把持久設備上的數據讀取到內存中的這個動作稱爲輸入(讀)Input操作
*文件到程序稱爲輸入
*d: IO操作
*把上面的這種輸入和輸出動作稱爲IO操作
按照數據單位分:字符流字節流
字節流 : 字節流可以操作任何數據,因爲在計算機中任何數據都是以字節的形式存儲的
字符流 : 字符流只能操作純字符數據,比較方便。
功能不同:節點流和處理流
節點流(不管是字符節點流還是字節節點流)可以從特定的數據源上讀取數據 處理流不能單獨使用在數據源上可以套用在節點流或者處理流上面(能用的流說明源代碼已經實現了)
3所有的流類型位於IO包下的四個抽象類
| 字節流 | 字符流 |
輸入流 | InputStream | Reader |
輸出流 | OutputStream | Writer |
5 四大抽象類中的方法
1)inputStream
基本方法
intread(); 讀取一個字節並且放在int 的低八位以整數的形式返回 0-255 -1 讀到頭
intread(byte[] buffer); 返回實際讀取的字節數如果讀前已經到末尾就返回 -1 讀到字符數組中
intread(byte[] buffer,int offset ,int length)throws IOException; 讀數組中的開始位置和長度
voidclose() 關閉流
longskip();跳過n個字節不讀返回實際跳過的字節數
2)outputStream
void write(int b) 寫一個字節並且放在int 的低8位
void write(byte [] b)
void write(byte [] b int offset ,int length) 讀數組中的開始位置和長度
void close() 關閉流
void flush();
3)Reader
int read() 讀取一個字符並且以整數的形式返回作爲整數讀取的字符,範圍在0 到 65535 之間)返回-1 說明到末尾了放在低16位
int read(char[] buf) 讀到字符數組中返回實際讀到的字符數
intread(char[] buffer,int offset ,int length)throws IOException; 讀數組中的開始位置和長度
void close() 關閉流
longskip();跳過n個字節不讀返回實際跳過的字節數
4)Writer
void writer (int c) 向輸出流中寫入一個字符數據該字節數據位參數的低16 位
void write(char [] b) 從字符類型的數組中的數據寫入輸出流
void write(char [] b int offset ,int length) 寫數組中的開始位置和長度到流中
void close() 關閉流
void flush();將輸出流中緩衝的數據全部寫出到目的地
5)flush方法和close方法區別
*a: flush()方法
* 用來刷新緩衝區的,刷新後可以再次寫出,只有字符流才需要刷新
*b: close()方法
* 用來關閉流釋放資源的的,如果是帶緩衝區的流對象的close()方法,不但會關閉流,還會再關閉流之前刷新緩衝區,關閉後不能再寫出 關閉流只用一個最大的那個流進行關閉
5)流的分別講解
5.1) * A: 字節輸出流FileOutputStream寫字節
*a: FileOutputStream
*寫入數據文件,學習父類方法,使用子類對象
*b: FileOutputStream構造方法
*作用:綁定輸出的輸出目的
*FileOutputStream(File file)
*創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。
*FileOutputStream(File file, boolean append)
*創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流,以追加的方式寫入。
*FileOutputStream(String name)
*創建一個向具有指定名稱的文件中寫入數據的輸出文件流。
*FileOutputStream(String name, boolean append)
* 創建一個向具有指定 name 的文件中寫入數據的輸出文件流,以追加的方式寫入。
*c: 流對象使用步驟
* 1. 創建流子類的對象,綁定數據目的
* 2. 調用流對象的方法write寫
* 3. close釋放資源
* d: 注意事項
* 流對象的構造方法,可以創建文件,如果文件存在,直接覆蓋文件的內容也進行覆蓋
5.2)字節輸出流FileOutputStream寫字節數組
*A: 字節輸出流FileOutputStream寫字節數組
*a: 方法介紹
* void write(byte[] b):將 b.length 個字節從指定的 byte 數組寫入此輸出流
* void write(byte[] b, int off, int len) :將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此輸出流。
*b: 案例代碼
/*
* FileOutputStream
* 寫入數據文件,學習父類方法,使用子類對象
*
* 子類中的構造方法: 作用:綁定輸出的輸出目的
* 參數:
* File 封裝文件
* String 字符串的文件名
*
* 流對象使用步驟
* 1.創建流子類的對象,綁定數據目的
* 2.調用流對象的方法write寫
* 3.close釋放資源
*
* 流對象的構造方法,可以創建文件,如果文件存在,直接覆蓋
5.3): 文件的續寫和換行符號
*a: 文件的續寫
* FileOutputStream構造方法, 的第二個參數中,加入true
*b: 換行符號
*在文件中,寫入換行,符號換行 \r\n
*\r\n 可以寫在上一行的末尾, 也可以寫在下一行的開頭
*c: 案例代碼
/*
* FileOutputStream 文件的續寫和換行問題
* 續寫: FileOutputStream構造方法, 的第二個參數中,加入true
* 在文件中,寫入換行,符號換行 \r\n
* \r\n 可以寫在上一行的末尾, 也可以寫在下一行的開頭
*/
publicclass FileOutputStreamDemo1 {
publicstatic void main(String[] args)throws IOException {
Filefile = new File("c:\\b.txt");
FileOutputStreamfos = new FileOutputStream(file,true);
fos.write("hello\r\n".getBytes());
fos.write("world".getBytes());
fos.close();
}
}
5.4)字節輸入流FileInputStream讀取字節
*A: 字節輸入流FileInputStream讀取字節
*a: 方法介紹 沒有追加的說法
*abstract int read() :
*從輸入流中讀取數據的下一個字節,返回-1表示文件結束
*int read(byte[] b)
*從輸入流中讀取一定數量的字節,並將其存儲在緩衝區數組 b 中。
*讀入緩衝區的字節總數,如果因爲已經到達文件末尾而沒有更多的數據,則返回 -1。
*int read(byte[] b, int off, int len)
*將輸入流中最多 len 個數據字節讀入 byte 數組。
*void close()
*關閉此輸入流並釋放與該流關聯的所有系統資源。
*b: 案例代碼
/*
* FileInputStream讀取文件
*
* 構造方法: 爲這個流對象綁定數據源
*
* 參數:
* File 類型對象
* String 對象
* 輸入流讀取文件的步驟
* 1.創建字節輸入流的子類對象
* 2.調用讀取方法read讀取
* 3.關閉資源
*
* read()方法,
* read()執行一次,就會自動讀取下一個字節
* 返回值,返回的是讀取到的字節, 讀取到結尾返回-1
讀出來的byte[] b 數組可以通過 new String(b) 來進行解碼
String(byte[] bytes, int offset, int length)
通過使用平臺的默認字符集解碼指定的 byte 子數組,構造一個新的String。
5.4)字符輸出流寫文本FileWriter類
*A: 字符輸出流寫文本FileWriter類
*a: 方法介紹
* void write(int c)
* 寫入單個字符
*void write(String str)
*寫入字符串
*void write(String str, int off, int len)
*寫入字符串的某一部分
*void write(char[] cbuf)
*寫入字符數組
*abstract void write(char[] cbuf, intoff, int len)
* 寫入字符數組的某一部分
*b: 案例代碼
/*
* 字符輸出流
* java.io.Writer 所有字符輸出流的超類
* 寫文件,寫文本文件
*
* 寫的方法 write
* write(int c) 寫1個字符
* write(char[] c)寫字符數組
* write(char[] c,int,int)字符數組一部分,開始索引,寫幾個
* write(String s) 寫入字符串
*
* Writer類的子類對象 FileWriter
*
* 構造方法: 寫入的數據目的
* File 類型對象
* String 文件名
*
* 字符輸出流寫數據的時候,必須要運行一個功能,刷新功能
* flush()
*/
5.5)字符輸入流讀取文本FileReader類
*A: 字符輸入流讀取文本FileReader類
*a: 方法介紹
* int read()
*讀取單個字符
*int read(char[] cbuf)
*將字符讀入數組
*abstract int read(char[] cbuf, int off,int len)
*將字符讀入數組的某一部分。
*b: 案例代碼
/*
* 字符輸入流讀取文本文件,所有字符輸入流的超類
* java.io.Reader
* 專門讀取文本文件
*
* 讀取的方法 : read()
* intread() 讀取1個字符
* intread(char[] c) 讀取字符數組
*
* Reader類是抽象類,找到子類對象 FileReader
*
* 構造方法: 綁定數據源
* 參數:
* File 類型對象
* String文件名
可以強制轉換進行解碼(char)直接轉 構造函數中可以直接寫文件的名字 因爲底層已經實現了字節流和轉換流來交節看源碼
*/
5.6)* a: 轉換流概述
*OutputStreamWriter 是字符流通向字節流的橋樑:可使用指定的字符編碼表,將要寫入流中的字符編碼成字節
* 將字符串按照指定的編碼表轉成字節,在使用字節流將這些字節寫出去
OutputStreamWriter寫文本文件
*A: OutputStreamWriter寫文本文件
*a: OutputStreamWriter
*java.io.OutputStreamWriter 繼承Writer類
*就是一個字符輸出流,寫文本文件
* write()字符,字符數組,字符串
*字符通向字節的橋樑,將字符流轉字節流
*OutputStreamWriter 使用方式
*構造方法:
*OutputStreamWriter(OuputStream out)接收所有的字節輸出流
*字節輸出流: FileOutputStream
*OutputStreamWriter(OutputStream out, String charsetName)
*String charsetName 傳遞編碼表名字 GBK UTF-8
* OutputStreamWriter 有個子類, FileWriter
*b: 案例代碼
publicclass OutputStreamWriterDemo {
publicstatic void main(String[] args)throws IOException {
// writeGBK();
writeUTF();
}
/*
* 轉換流對象OutputStreamWriter寫文本
* 採用UTF-8編碼表寫入
*/
轉換流_字節轉字符流過程
*A: 轉換流_字節轉字符流過程
*a: InputStreamReader
*java.io.InputStreamReader 繼承 Reader
*字符輸入流,讀取文本文件
*字節流向字符的敲了,將字節流轉字符流
*讀取的方法:
* read() 讀取1個字符,讀取字符數組
*技巧
*OuputStreamWriter寫了文件
*InputStreamReader讀取文件
*OutputStreamWriter(OutputStream out)所有字節輸出流
*InputStreamReader(InputStream in) 接收所有的字節輸入流
*可以傳遞的字節輸入流: FileInputStream
* InputStreamReader(InputStream in,String charsetName) 傳遞編碼表的名字
*b: 圖解
* 詳見day24_source/轉換流.JPG圖片
* OutputStreamWriter 有個子類, FileWriter
InputStreamReader 有個子類 FileReader
區別
*OutputStreamWriter和InputStreamReader是字符和字節的橋樑:也可以稱之爲字符轉換流。字符轉換流原理:字節流+編碼表。
*FileWriter和FileReader:作爲子類,僅作爲操作字符文件的便捷類存在。
當操作的字符文件,使用的是默認編碼表時可以不用父類,而直接用子類就完成操作了,簡化了代碼。
* 以下三句話功能相同
* InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"));//默認字符集。
* InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
* FileReader fr = newFileReader("a.txt");
InputSteamReader讀取文本文件
*A: InputSteamReader讀取文本文件
*a: 案例代碼
publicclass InputStreamReaderDemo {
publicstatic void main(String[] args) throws IOException {
// readGBK();
readUTF();
}
/*
* 轉換流,InputSteamReader讀取文本
* 採用UTF-8編碼表,讀取文件utf
*/
publicstatic void readUTF()throws IOException{
//創建自己輸入流,傳遞文本文件
FileInputStreamfis = new FileInputStream("c:\\utf.txt");
//創建轉換流對象,構造方法中,包裝字節輸入流,同時寫編碼表名
InputStreamReaderisr = new InputStreamReader(fis,"UTF-8");
char[]ch = new char[1024];
intlen = isr.read(ch);
System.out.println(newString(ch,0,len));
isr.close();
}
/*
* 轉換流,InputSteamReader讀取文本
* 採用系統默認編碼表,讀取GBK文件
*/
public static voidreadGBK()throws IOException{
//創建自己輸入流,傳遞文本文件
FileInputStreamfis = new FileInputStream("c:\\gbk.txt");
//創建轉換流對象,構造方法,包裝字節輸入流
InputStreamReaderisr = new InputStreamReader(fis);
char[]ch = new char[1024];
int len =isr.read(ch);
System.out.println(newString(ch,0,len));
isr.close();
}
}
5.6)字節輸出流緩衝流BufferedOutputStream
*A: 字節輸出流緩衝流BufferedOutputStream
*a: BufferedOutputStream
*字節輸出流的緩衝流
*java.io.BufferedOuputStream 作用: 提高原有輸出流的寫入效率
*BufferedOuputStream 繼承 OutputStream
*方法,寫入 write 字節,字節數組
*構造方法:
*BufferedOuputStream(OuputStream out)
*可以傳遞任意的字節輸出流, 傳遞的是哪個字節流,就對哪個字節流提高效率
5.7)字節輸入流緩衝流BufferedInputStream
*A: 字節輸入流緩衝流BufferedInputStream
*a: BufferedInputStream
*字節輸入流的緩衝流
*繼承InputStream,標準的字節輸入流
*讀取方法 read() 單個字節,字節數組
*構造方法:
*BufferedInputStream(InputStream in)
*可以傳遞任意的字節輸入流,傳遞是誰,就提高誰的效率
* 可以傳遞的字節輸入流 FileInputStream)
5.8)字符輸出流緩衝流BufferedWriter
*A: 字符輸出流緩衝流BufferedWriter
*a: BufferedWriter
*字符輸出流緩衝區流
*java.io.BufferedWriter 繼承 Writer
*寫入方法 write () 單個字符,字符數組,字符串
*構造方法:
*BufferedWriter(Writer w)傳遞任意字符輸出流
*傳遞誰,就高效誰
*能傳遞的字符輸出流 FileWriter, OutputStreamWriter
12字符輸出流緩衝流BufferedWriter特有方法newLine
*A: 字符輸出流緩衝流BufferedWriter特有方法newLine
*a: 方法介紹
*void newLine() 寫換行
*newLine()文本中換行, \r\n也是文本換行
*方法具有平臺無關性
*Windows \r\n
*Linux \n
*newLine()運行結果,和操作系統是相互關係
*JVM: 安裝的是Windows版本,newLine()寫的就是\r\n
*安裝的是Linux版本,newLine()寫的就是\n
/*
5.9)字符輸入流緩衝流BufferedReader
*A: 字符輸入流緩衝流BufferedReader
*a: 概述
*從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取
*public String readLine() 讀取一個文本行,包含該行內容的字符串,不包含任何行終止符,如果已到達流末尾,則返回 null
###14字符輸入流緩衝流BufferedReader讀取文本行
*A: 字符輸入流緩衝流BufferedReader讀取文本行
*a: BufferedReader
*字符輸入流緩衝流
*java.io.BufferedReader 繼承 Reader
*讀取功能 read() 單個字符,字符數組
*構造方法:
*BufferedReader(Reader r)
*可以任意的字符輸入流
FileReader InputStreamReader
*BufferedReader自己的功能
*String readLine() 讀取文本行 \r\n
*方法讀取到流末尾,返回null
* b: 小特點
* 獲取內容的方法一般都有返回值
* int 沒有返回的都是負數
* 引用類型找不到返回null
* boolean 找不到返回false
5.10)對象的序列化與反序列化
*A: 對象的序列化與反序列化
*a: 基本概念
*對象的序列化
*對象中的數據,以流的形式,寫入到文件中保存過程稱爲寫出對象,對象的序列化
*ObjectOutputStream將對象寫道文件中,實現序列化
*對象的反序列化
*在文件中,以流的形式,將對象讀出來,讀取對象,對象的反序列化
*ObjectInputStream 將文件對象讀取出來
ObjectOutputStream流寫對象
*A: ObjectOutputStream流寫對象
*a: 簡單介紹
* IO流對象,實現對象Person序列化,和反序列化
* ObjectOutputStream 寫對象,實現序列化
* ObjectInputStream 讀取對象,實現反序列化
ObjectInputStream流讀取對象
*A: ObjectInputStream流讀取對象
*a: 簡單介紹
*ObjectInputStream
*構造方法:ObjectInputStream(InputStream in)
*傳遞任意的字節輸入流,輸入流封裝文件,必須是序列化的文件
*Object readObject() 讀取對象
*b: 案例代碼
/*
* IO流對象,實現對象Person序列化,和反序列化
* ObjectOutputStream 寫對象,實現序列化
* ObjectInputStream 讀取對象,實現反序列化
*/
需要掌握流的總結
FileInputStream | FileOutputStream |
InputStreamReader | OutputStreamWriter |
FileReader | FileWriter |
ObjectInputStream | * ObjectOutputStream |
| PrintStream PrintWriter |
DataInputStream | DataOutputStream |
BufferedReader | BufferedWriter |
ByteArrayInputStream | ByteArrayOutputStream |
BufferedInputStream | BufferedOutputStream |
5.11)靜態不能序列化
*A: 靜態不能序列化
*a: 原因
*序列化是把對象數據進行持久化存儲
*靜態的東西不屬於對象,而屬於類
transient關鍵字
*A: transient關鍵字
*a: 作用
*被transient修飾的屬性不會被序列化
*transient關鍵字只能修飾成員變量
Serializable接口的含義
*A:Serializable接口的含義
*a: 作用
*給需要序列化的類上加標記。該標記中沒有任何抽象方法
*只有實現了 Serializable接口的類的對象才能被序列化
序列化中的序列號衝突問題
*A: 序列化中的序列號衝突問題
*a: 問題產生原因
*當一個類實現Serializable接口後,創建對象並將對象寫入文件,之後更改了源代碼(比如:將成員變量的修飾符有private改成public),
再次從文件中讀取對象時會報異常
*見day25_source文件夾下的"序列號的衝突.JPG"文件
序列化中自定義的序列號
*A: 序列化中自定義的序列號
*a: 定義方式
*private static final long serialVersionUID = 1478652478456L;
* 這樣每次編譯類時生成的serialVersionUID值都是固定的
### 對象流
Java 提供了對象流,是處理流,可以簡化對象的IO。對象流要求參與對象序列化的類必須實現序列化接口
> 注意:實現序列化接口的類最好添加序列化版本號 serialVersionUID
> 瞬態關鍵字修飾的屬性在序列化時候被忽略掉。
5.12)打印流和特性
*A: 打印流和特性
*a: 概述
*打印流添加輸出數據的功能,使它們能夠方便地打印各種數據值表示形式.
*打印流根據流的分類:
*字節打印流 PrintStream
*字符打印流 PrintWriter
*方法:
*void print(String str): 輸出任意類型的數據,
*void println(String str): 輸出任意類型的數據,自動寫入換行操作
*b: 特點
*此流不負責數據源,只負責數據目的
*爲其他輸出流,添加功能
*永遠不會拋出IOException,但是可能拋出別的異常
*兩個打印流的方法,完全一致
*構造方法,就是打印流的輸出目的端
* PrintStream構造方法
*接收File類型,接收字符串文件名,接收字節輸出流OutputStream
*PrintWriter構造方法
* 接收File類型,接收字符串文件名,接收字節輸出流OutputStream, 接收字符輸出流Writer
* b: 結果分析
*println數組,只有打印字符數組時只有容,其餘均打印數組的地址
*因爲api中定義了打印字符數組的方法,其底層是在遍歷數組中的元素
* 而其他打印數組的方法,都是將數組對象編程Object,其底層再將對象編程String,調用了String s = String.valueOf(x);方法
PrintWriter(File file) |
PrintWriter(File file, String csn) |
PrintWriter(OutputStream out) |
PrintWriter(OutputStream out, boolean autoFlush) |
PrintWriter(String fileName) |
PrintWriter(String fileName, String csn) |
PrintWriter(Writer out) |
PrintWriter(Writer out, boolean autoFlush) |
* 打印流,可以開啓自動刷新功能
* 滿足2個條件:
* 1. 輸出的數據目的必須是流對象
* OutputStream Writer
* 2. 必須調用println,printf,format三個方法中的一個,啓用自動刷新
6 字符節點流可以直接連數據源的(不能和字節節點流連用) 字符處理流只能套結在字符節點流或者字符處理流上 (而轉換流除外)它可以把字符節點流轉化成字符處理流轉化流也可以套結在字節處理流上把()
printWriter() 裏面封裝了可以直接放文件名具體的看構造函數
publicPrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(newFileOutputStream(fileName))),
false);
}
7 RamdomAccessFile 與流
7.1. RamdomAccessFile 可以隨機訪問(可以在文件的任何位置開始讀寫),而流只能單向讀寫,讀寫過去就不能返回頭重寫讀寫了。流讀寫是分開的兩個類型。
7.2. 流是一套可以擴展的體系,提供了無限的擴展功能流。而RAF只能出來byte數據和基本類型數據。RAF功能沒有提供豐富擴展功能。
### 節點流與處理流
- **節點流**:是連接到數據源進行最基本的IO功能的流,是其他處理流的基礎。
- **處理流**:是流的功能擴展,有很多處理流,體現了流的豐富擴展功能。處理流因爲是基於其他流進行的擴展功能,所以處理流不能單獨工作,必須依賴其他的流。
按照功能不同分爲節點流和處理流
節點流連接數據源的
處理流是節點流之上的處理流套結在節點流或者處理流之上的
IO流得操作都是從頭到尾得不能通過指針來定位 寫的時候
1 PrintWriter(String fileName, String csn)
創建具有指定文件名稱和字符集且不帶自動行刷新的新 PrintWriter。
2 PrintWriter(File file)
使用指定文件創建不具有自動行刷新的新 PrintWriter。
3
|
PrintWriter(String fileName) |
8 編碼表
* A: 編碼表
* a: 定義:
* 生活中字符和計算機二進制的對應關係表,就是編碼表
* b: 分類
* 1、ascii: 一個字節中的7位就可以表示。對應的字節都是正數。0-xxxxxxx
* 2、iso-8859-1:拉丁碼錶 latin,用了一個字節用的8位。1-xxxxxxx 負數。
* 3、GB2312:簡體中文碼錶。包含6000-7000中文和符號。用兩個字節表示。兩個字節第一個字節是負數,第二個字節可能是正數
* GBK:目前最常用的中文碼錶,2萬的中文和符號。用兩個字節表示,其中的一部分文字,第一個字節開頭是1,第二字節開頭是0
* GB18030:最新的中文碼錶,目前還沒有正式使用。
* 4、unicode:國際標準碼錶:無論是什麼文字,都用兩個字節存儲。
* Java中的char類型用的就是這個碼錶。char c = 'a';佔兩個字節。
* Java中的字符串是按照系統默認碼錶來解析的。簡體中文版 字符串默認的碼錶是GBK。
* 5、UTF-8:基於unicode,一個字節就可以存儲數據,不要用兩個字節存儲,而且這個碼錶更加的標準化,在每一個字節頭加入了編碼信息(後期到api中查找)。
* 6、能識別中文的碼錶:GBK、UTF-8;正因爲識別中文碼錶不唯一,涉及到了編碼解碼問題。
* 對於我們開發而言;常見的編碼 GBK UTF-8 ISO-8859-1
* 文字--->(數字) :編碼。“abc”.getBytes() byte[]
* (數字)--->文字 : 解碼。 byte[]b={97,98,99} new String(b)
9流對象的操作規律
* A: IO流對象的操作規律
* a: 明確一:要操作的數據是數據源還是數據目的。
* 源:InputStream Reader
* 目的:OutputStreamWriter
* 先根據需求明確要讀,還是要寫。
* b: 明確二:要操作的數據是字節還是文本呢?
* 源:
* 字節:InputStream
* 文本:Reader
* 目的:
* 字節:OutputStream
* 文本:Writer
* c: 明確三:明確數據所在的具體設備。
* 源設備:
* 硬盤:文件 File開頭。
* 內存:數組,字符串。
* 鍵盤:System.in;
* 網絡:Socket
* 目的設備:
* 硬盤:文件 File開頭。
* 內存:數組,字符串。
* 屏幕:System.out
* 網絡:Socket
* 完全可以明確具體要使用哪個流對象。
* d: 明確四:是否需要額外功能呢?
* 額外功能:
* 轉換嗎?轉換流。InputStreamReader OutputStreamWriter
* 高效嗎?緩衝區對象。BufferedXXX
* 已經明確到了具體的體系上。