寫在前面
從Java 1.0開始,引入java.io包;到Java 1.4再擴展了java.nio包;再到java 1.7又添加了新的流類,使得Java的流機制變得十分強大。
一、"流"概念
James Gosling所著《Java程序設計》中描述Java I/O流模式圖如下。Program是中間環節,用于對Source進行處理,然后輸出到Dest處。
Java中的"流"就是指把數據從一個對象移動到另一個對象的流動模式的抽象。其實Java的流模式用水流或者電流模型來解釋是很容易理解的。
James Gosling的Java流模式圖與水流模式圖概念映射。數據源(data source)即水庫,數據目的地(data destination)就是臉盆,數據(data)就是水,流(stream)實例化就是在管子中流動的水流。
輸入流(input stream)就是用水泵從水庫中抽出來要到水管中的水,輸出流(output stream)經過水龍頭將要達到臉盆中的水,計算機內存(memory)就是上圖中的水流管道,關閉輸入流(close input stream)就是關閉水泵開關,關閉輸出流(close output stream)就是關閉關閉水龍頭開關。
更進一步說,具體的水庫和臉盆分別對應于Java中輸入流對象和輸出流對象。水流可以分成一粒一粒的水分子,這些水分子映射成計算機二進制位(bit)0/1,其組成的水滴映射成計算機字節流(字節是計算機儲存信息的基本單位)。字節流和字符流在物理層面的實現都是比特流,二進制數據流可以認為是字節流,而字符流是遵循unicode編碼規則的字節流。因此計算機中的"流"概念實際上就是指字節數據(bytes data)從源對象對按順序流向目標對象的一種流動形式。
二、流的分類
1、按流的方向分為:輸入流、輸出流
判斷當前流是輸入流還是輸出流的依據是二進制數據相對于計算機內存的位置,輸入流是輸入計算機內存是二進制數據,輸出流是從計算機內存輸出的二進制數據。而計算機程序在運行期間會儲存在到計算機內存中,因此總的來說就是數據的來源、取向是相對程序而言的。比如鍵盤鍵入數據屬于輸入流,內存數據持久化到磁盤中屬于輸出流。
說明:本圖片取自互聯網,糾正補充為流的來源有網絡連接、內存塊、磁盤(文件)、鍵盤等;流的去向也基本是這些。
2、按流處理數據的單位分為:字節流、字符流
從物理層面來看,流中數據都是二進制比特流。而計算機中儲存信息的基本單位是字節(Byte)。因此,可以認為計算機中信息傳輸在底層是靠字節流來實現的。字符流只是通過不同的字符編碼方式,對字節流的封裝,即字符流的實現還是得依靠字節流。
3、按流的功能分為:節點流(又稱低級流)、過濾流(又稱高級流、處理流、包裝流)
節點流(Node Stream)是流管道兩端直接連接data source和data destination上的,即為取放數據的真實載體,在流通道本身不對數據做任何加工,因而也被稱為低級流。
節點流從一個特定的數據源讀寫數據。即節點流是直接操作文件,網絡等的流,例如FileInputStream和FileOutputStream,他們直接從文件中讀取或往文件中寫入字節流。
“連接”在已存在的流(節點流或處理流)之上通過對數據的處理為程序提供更為強大的讀寫功能。過濾流是使用一個已經存在的輸入流或輸出流連接創建的,過濾流就是對節點流進行一系列的包裝。例如BufferedInputStream和BufferedOutputStream,使用已經存在的節點流來構造,提供帶緩沖的讀寫,提高了讀寫的效率,以及DataInputStream和DataOutputStream,使用已經存在的節點流來構造,提供了讀寫Java中的基本數據類型的功能。他們都屬于過濾流。
public static void main(String[] args) throws IOException { // 節點流FileOutputStream直接以A.txt作為數據源操作 FileOutputStream fileOutputStream = new FileOutputStream("A.txt"); // 過濾流BufferedOutputStream進一步裝飾節點流,提供緩沖寫 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream( fileOutputStream); // 過濾流DataOutputStream進一步裝飾過濾流,使其提供基本數據類型的寫 DataOutputStream out = new DataOutputStream(bufferedOutputStream); out.writeInt(3); out.writeBoolean(true); out.flush(); out.close(); // 此處輸入節點流,過濾流正好跟上邊輸出對應,讀者可舉一反三 DataInputStream in = new DataInputStream(new BufferedInputStream( new FileInputStream("A.txt"))); System.out.println(in.readInt()); System.out.println(in.readBoolean()); in.close(); }
理解:
-
FileOutputStream
是根據二進制010101一個一個字節處理 -
BufferedOutputStream
是對字節封裝成buffered,以緩沖區處理 -
DataOutputStream
是以字符串形式,類似(“hello”)處理。
4、字節流與字符流區別
- 字節流默認是不帶緩沖區的,而字符流默認是帶緩沖區的。
- 字節流是底層數據流,是數據有意義的最小單位。字符流是字節流的包裝,底層實現是字節流。
- 字節流讀取的時候,讀到一個字節就返回一個字節;字符流使用了字節流讀到一個或多個字節(中文對應的字節數是兩個,在UTF-8碼表中是3個字節)時。先去查指定的編碼表,將查到的字符返回。
- 字節流可以處理所有類型數據,如:圖片,MP3,AVI視頻文件,而字符流只能處理字符數據。只要是處理純文本數據,就要優先考慮使用字符流,除此之外都用字節流。文本文件可以用字節流來實現,當然使用字符流速度會更快。
三、流的方法
1、字節流
字節輸入流類:FileInputStream、BufferedInputStream和DataInputStream
FileInputStream:此類用于從本地文件系統中讀取文件內容。
構造方法:
-
FileInputStream(File file):
打開一個到實際文件的連接來創建一個FileInputStream,該文件通過文件系統中的File對象file指定。 -
FileInputStream(String name):
打開一個到實際文件的連接來創建一個FileInputStream,該文件通過文件系統中的路徑名name指定。
常用方法:
-
int available():
返回下一次對此輸入流調用的方法不受阻塞地從此輸入流讀取(或跳過)的估計剩余字節數。 -
void close():
關閉此文件輸入流并釋放與該流關聯的所有系統資源。
BufferedInputStream:此類本身帶有一個緩沖區,在讀取數據時,先放到緩沖區中,可以減少對數據源的訪問,提高運行的效率。
構造方法:
-
BufferedInputStream(InputStream in):
創建一個BufferedInputStream并保存其參數,即輸入流in,以便將來使用。 -
BufferedInputStream(InputStream in,int size):
創建一個具有指定緩沖區大小的BufferedInputStream并保存其參數,即輸入流in,以便將來使用。
常用方法:
-
int available():
返回下一次對此輸入流調用的方法不受阻塞地從此輸入流讀取(或跳過)的估計剩余字節數。 -
void close():
關閉此輸入流并釋放與該流關聯的所有系統資源。 -
int read():
從輸入流中讀取數據的下一個字節。 -
int read(byte[] b,int off,int len):
從此字節輸入流中給定偏移量處開始將各字節讀取到指定的byte數組中。
DataInputStream:該類提供一些基于多字節讀取方法,從而可以讀取基本數據類型的數據。
構造方法:
-
DataInputStream(InputStream in):
使用指定的底層InputStream創建一個DataInputStream。
常用方法:
-
int read(byte[] b):
從包含的輸入流中讀取一定數量的字節,并將它們存儲到緩沖區數組b中。 -
int read(byte[] b,int off,int len):
從包含的輸入流中將最多len個字節讀入一個byte數組中。
字節輸出流類:FileOutputStream、BufferedOutputStream和DataOutputStream
FileOutputStream:此類用于從本地文件系統的文件中寫入數據。
構造方法:
-
FileOutputStream(File file):
創建一個向指定File對象表示的文件中寫入數據的文件輸出流。FileOutputStream(String name):創建一個向具有指定名稱的文件中寫入數據的輸出文件流。
常用方法:
-
void close():
關閉此文件輸出流并釋放與此流有關的所有系統資源。 -
FileDescriptor getFD():
返回與此流有關的文件描述符。 -
void write(byte[] b):
將b.length個字節從指定byte數組寫入此文件輸出流中。 -
void write(byte[] b,int off,int len):
將指定byte數組中從偏移量off開始的len個字節寫入此文件輸出流。 -
void write(int b):
將指定字節寫入此文件輸出流。
BufferedOutputStream:此類本身帶有一個緩沖區,在寫入數據時,先放到緩沖區中,實現緩沖的數據流。
構造方法:
-
BufferedOutputStream(OutputStream out):
創建一個新的緩沖輸出流,來將數據寫入指定的底層輸入流。 -
BufferedOutputStream(OutputStream out,int size):
創建一個新的緩沖輸出流,來將具有指定緩沖區大小的數據寫入指定的底層輸出流。
常用方法:
-
void flush():
刷新此緩沖的輸出流。 -
void write(byte[] b,int off,int len):
將指定byte數組中從偏移量off開始的len個字節寫入此緩沖的輸出流。 -
void write(int b):
將指定的字節寫入此緩沖的輸出流。
DataOutputStream(OutputStream out):創建一個新的數據輸出流,將數據寫入指定基礎輸出流。
常用方法:
-
void flush():
清空此數據輸出流。 -
int size():
返回計數器written的當前值,即到目前為止寫入此數據輸出流的字節數。 -
void write(byte[] b,int off,int len):
將指定byte數組中從偏移量off開始的len個字節寫入基礎輸出流。 -
void write(int b):
將指定字節(參數b的八個低位)寫入基礎輸出流。
2、字符流
FileReader:用來讀取字符文件的便捷類。此類的構造方法假定默認字符編碼和默認字節緩沖區大小都是適當的
構造方法:
FileReader(File file):在給定從中讀取數據的File的情況下創建一個新的FileReader。FileReader(String fileName):在給定從中讀取數據的文件名的情況下創建一個新的FileReader。
BufferedReader類是Reader類的子類,為Reader對象添加字符緩沖器,為數據輸入分配內存存儲空間,存取數據更為有效。
構造方法:
-
BufferedReader(Reader in):
創建一個使用默認大小輸入緩沖區的緩沖字符輸入流。 -
BufferedReader(Reader in,int sz):
創建一個使用指定大小輸入緩沖區的緩沖字符輸入流。
操作方法:
-
void close():
關閉該流并釋放與之關聯的所有資源。 -
void mark(int readAheadLimit):
標記流中的當前為止。 -
boolean markSupported();
判斷此流是否支持mark()操作。 -
int read():
讀取單個字符。 -
int read(char[] cbuf,int off,int len):
將字符讀入數組的某一部分。 -
String readLine():
讀取一個文本行。 -
boolean ready():
判斷此流是否已準備好被讀取。 -
void reset():
將流重置到最新的標記。 -
long skip(long n):
跳過字符。
FileWriter:用來寫入字符文件的便捷類,可用于寫入字符流。
構造方法:
FileWriter(File file):
根據給定的File對象構造一個FileWriter對象。
FileWriter(String filename):
根據給定的文件名構造一個FileWriter對象。
BufferedWriter: 將文本寫入字符輸出流,緩沖各個字符,從而提供單個字符、數組和字符串的高效寫入。
緩沖流的目的:
操作流的時候,習慣定義一個byte/char數組。
int read(): 每次都從磁盤文件中讀取一個字節。 直接操作磁盤文件性能極低。
解決方案:定義一個數組作為緩沖區。
byte[] buffer = new byte[1024];該數組其實就是一個緩沖區。
一次性從磁盤文件中讀取1024個字節。如此以來,操作磁盤文件的次數少了,性能得以提升。提供的默認緩存區大小是8192(1024*8),我們一般不用修改大小
public class BufferStreamDemo { public static void main(String[] args) throws Exception { File file = new File("file/aaa.txt"); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); out.write("中國".getBytes()); out.close(); BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); byte[] buffer = new byte[1024]; int len = -1; while((len = in.read(buffer)) != -1){ System.out.println(new String(buffer, 0, len)); } in.close(); } } public class BufferCharacterDemo { public static void main(String[] args) throws Exception { File file = new File("file/aaa.txt"); BufferedWriter in = new BufferedWriter(new FileWriter(file,true)); in.newLine();//用來換行等同于‘ " in.write("美國"); in.newLine(); in.write("馬來西亞"); in.close(); BufferedReader out = new BufferedReader(new FileReader(file)); String line = null; //按行讀取 while((line = out.readLine()) != null){ System.out.println(line); } out.close(); } }
4、流相關設計模式
處理流/包裝流(相對于節點流更高級) 裝飾設計模式/包裝模式:
- 隱藏了底層的節點流的差異,并對外提供了更方便的輸入/輸出功能,讓我們只關心高級流的操作.
- 使用處理流包裝了節點流,程序直接操作處理流,讓節點流與底層的設備做IO操作.
- 只需要關閉處理流即可.
參考:https://blog.csdn.net/dreamzuora/article/details/79691702
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!
原文鏈接:https://blog.csdn.net/mingyuli/article/details/120261771