一、流
1.流的概念
流是一組有順序的,有起點和終點的字節集合,是對數據傳輸的總稱或抽象。即數據在兩設備間的傳輸稱為流,流的本質是數據傳輸,根據數據傳輸特性將流抽象為各種類,方便更直觀的進行數據操作。
2.流的分類
根據處理數據類型的不同分為:字符流和字節流,字符流處理的單元為 2 個字節的 Unicode 字符,分別操作字符、字符數組或字符串,而字節流處理單元為 1 個字節,操作字節和字節數組。
根據數據流向不同分為:輸入流和輸出流
3.字符流和字節流
3.1 字符流的由來: 因為數據編碼的不同,而有了對字符進行高效操作的流對象。本質其實就是基於字節流讀取時,去查了指定的碼表。 Java 內用 Unicode 編碼存儲字符,字符流處理類負責將外部的其他編碼的字符流和 java 內 Unicode 字符流之間的轉換。
3.2 字節流和字符流的區別:
(1)讀寫單位不同:字節流以字節(8bit)為單位,字符流以字符為單位,根據碼表映射字符,一次可能讀多個字節。
(2)處理對象不同:字節流能處理所有類型的數據(如圖片、avi等),而字符流只能處理字符類型的數據。
(3)字節流在操作的時候本身是不會用到緩沖區的,是文件本身的直接操作的;而字符流在操作的時候下后是會用到緩沖區的,是通過緩沖區來操作文件,我們將在下面驗證這一點。
結論:優先選用字節流。首先因為硬盤上的所有文件都是以字節的形式進行傳輸或者保存的,包括圖片等內容。但是字符只是在內存中才會形成的,所以在開發中,字節流使用廣泛。從效率而言,字符流(一次可以處理一個緩沖區)一次操作比字節流(一次一個字節)效率高。
4.輸入流和輸出流
對輸入流只能進行讀操作,對輸出流只能進行寫操作,程序中需要根據待傳輸數據的不同特性而使用不同的流。輸入輸出流的流向是相對於程序而言。
輸入流:InputStream 讀入 就是從外部文件中讀取數據到程序。對應 Reader;
輸出流: OutputStream 寫出 就是將數據從程序寫入到外部文件。對應 Writer;
二、字節流
1.字節流概述
1)字節流和字符流的基本操作是相同的,但是要想操作媒體流就需要用到字節流。
2)字節流因為操作的是字節,所以可以用來操作媒體文件。(媒體文件也是以字節存儲的)
3)讀寫字節流:InputStream 輸入流(讀)和OutputStream 輸出流(寫)
4)字節流操作可以不用刷新流操作。
5)InputStream特有方法:
int available(); //返回文件中的字節個數
注:可以利用此方法來指定讀取方式中傳入數組的長度,從而省去循環判斷。但是如果文件較大,而虛擬機啟動分配的默認內存一般為64M。當文件過大時,此數組長度所占內存空間就會溢出。所以,此方法慎用,當文件不大時,可以使用
2. 字節流緩沖區
1)緩沖區的出現時為了提高流的操作效率而出現的.
2)需要被提高效率的流作為參數傳遞給緩沖區的構造函數
3)在緩沖區中封裝了一個數組,存入數據后一次取出
3.輸入字節流InputStream
定義和結構說明:
從輸入字節流的繼承圖可以看出:InputStream 是所有的輸入字節流的父類,它是一個抽象類。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三種基本的介質流,它們分別從Byte 數組、StringBuffer、和本地文件中讀取數據。
PipedInputStream 是從與其它線程共用的管道中讀取數據,與Piped 相關的知識后續單獨介紹。
ObjectInputStream 和所有FilterInputStream的子類都是裝飾流(裝飾器模式的主角)。意思是FileInputStream類可以通過一個String路徑名創建一個對象,FileInputStream(String name)。
DataInputStream必須裝飾一個類才能返回一個對象,DataInputStream(InputStream in)。
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); //定位文件位置 InputStream in=new FileInputStream(f); //創建字節輸入流連接到文件 byte[] b=new byte[1024]; //定義一個數組,用來存放讀入的數據 byte數組的大小也可以根據文件大小來定 (int)f.length() int count =0; int temp=0; while((temp=in.read())!=(-1)){ //in.read()是逐字節讀的。當讀到文件末尾時候返回-1 b[count++]=(byte)temp; //將讀到的字節存儲到byte數組中 } in.close(); //關閉流 System.out.println(new String(b)); //打印讀取到的字節 }
注意:當讀到文件末尾的時候會返回-1.正常情況下是不會返回-1的。
加入字節緩沖輸入流,提高了讀取效率
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); //定位文件位置 InputStream in=new FileInputStream(f); //創建字節輸入流連接到文件 BufferedInputStream bis=new BufferedInputStream(in); //創建緩沖字節流 byte[] b=new byte[1024]; //定義一個數組,用來存放讀入的數據 byte數組的大小也可以根據文件大小來定 (int)f.length() int count =0; int temp=0; bis.read(); while((temp=bis.read())!=(-1)){ //in.read()是逐字節讀的。當讀到文件末尾時候返回-1 b[count++]=(byte)temp; //將讀到的字節存儲到byte數組中 } bis.close(); //關閉緩沖字節流 in.close(); //關閉流 System.out.println(new String(b)); //打印讀取到的字節 }
4.輸出字節流OutputStream
定義和結構說明:
IO 中輸出字節流的繼承圖可見上圖,可以看出:OutputStream 是所有的輸出字節流的父類,它是一個抽象類。
ByteArrayOutputStream、FileOutputStream是兩種基本的介質流,它們分別向Byte 數組、和本地文件中寫入數據。
PipedOutputStream 是向與其它線程共用的管道中寫入數據,
ObjectOutputStream 和所有FilterOutputStream的子類都是裝飾流。具體例子跟InputStream是對應的。
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 OutputStream out = new FileOutputStream(f); // 創建字節輸出流連接到文件 String str = "hhhhhhh"; byte[] b = str.getBytes(); //講數據存入byte數組 out.write(b); //寫數據 out.close(); //關閉流 }
加入字節緩沖輸入流,提高了讀取效率
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 OutputStream out = new FileOutputStream(f); // 創建字節輸出流連接到文件 BufferedOutputStream bos=new BufferedOutputStream(out); String str = "hhhhhhh"; byte[] b = str.getBytes(); //講數據存入byte數組 bos.write(b); //寫數據 bos.close(); //關閉緩沖流 out.close(); //關閉流 }
5.幾個特殊的輸入流類分析
LineNumberInputStream
主要完成從流中讀取數據時,會得到相應的行號,至於什么時候分行、在哪里分行是由改類主動確定的,並不是在原始中有這樣一個行號。在輸出部分沒有對應的部分,我們完全可以自己建立一個LineNumberOutputStream,在最初寫入時會有一個基准的行號,以后每次遇到換行時會在下一行添加一個行號,看起來也是可以的。好像更不入流了。
PushbackInputStream
其功能是查看最后一個字節,不滿意就放入緩沖區。主要用在編譯器的語法、詞法分析部分。輸出部分的BufferedOutputStream 幾乎實現相近的功能。
StringBufferInputStream
已經被Deprecated,本身就不應該出現在InputStream部分,主要因為String 應該屬於字符流的范圍。已經被廢棄了,當然輸出部分也沒有必要需要它了!還允許它存在只是為了保持版本的向下兼容而已。
SequenceInputStream
可以認為是一個工具類,將兩個或者多個輸入流當成一個輸入流依次讀取。完全可以從IO 包中去除,還完全不影響IO 包的結構,卻讓其更“純潔”――純潔的Decorator 模式。
三、字符流
1.字符輸入流Reader
定義和說明:
在上面的繼承關系圖中可以看出:Reader 是所有的輸入字符流的父類,它是一個抽象類。
CharReader、StringReader是兩種基本的介質流,它們分別將Char 數組、String中讀取數據。
PipedReader 是從與其它線程共用的管道中讀取數據。
BufferedReader 很明顯就是一個裝飾器,它和其子類負責裝飾其它Reader 對象。
FilterReader 是所有自定義具體裝飾流的父類,其子類PushbackReader 對Reader 對象進行裝飾,會增加一個行號。
InputStreamReader 是一個連接字節流和字符流的橋梁,它將字節流轉變為字符流。FileReader可以說是一個達到此功能、常用的工具類,在其源代碼中明顯使用了將FileInputStream 轉變為Reader 的方法。我們可以從這個類中得到一定的技巧。Reader 中各個類的用途和使用方法基本和InputStream 中的類使用一致。后面會有Reader 與InputStream 的對應關系。
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 char[] ch = new char[100]; //定義一個數組,用來存放讀入的數據 Reader read = new FileReader(f); //創建字符輸入流連接到文件 int count = read.read(ch); //讀操作 read.close(); //關閉流 System.out.println("讀入的長度為:" + count); System.out.println("內容為" + new String(ch, 0, count)); }
加入緩沖流 提高效率。
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 char[] ch = new char[100]; //定義一個數組,用來存放讀入的數據 Reader read = new FileReader(f); //創建字符輸入流連接到文件 BufferedReader bfr=new BufferedReader(read); //創建緩沖流 int count = bfr.read(ch); //讀操作 bfr.close(); //關閉緩沖流 read.close(); //關閉流 System.out.println("讀入的長度為:" + count); System.out.println("內容為" + new String(ch, 0, count)); }
2.字符輸出流Writer
定義和說明:
在上面的關系圖中可以看出:Writer 是所有的輸出字符流的父類,它是一個抽象類。
CharArrayWriter、StringWriter 是兩種基本的介質流,它們分別向Char 數組、String 中寫入數據。
PipedWriter 是向與其它線程共用的管道中寫入數據,
BufferedWriter 是一個裝飾器為Writer 提供緩沖功能。
PrintWriter 和PrintStream 極其類似,功能和使用也非常相似。
OutputStreamWriter 是OutputStream 到Writer 轉換的橋梁,它的子類FileWriter 其實就是一個實現此功能的具體類(具體可以研究一SourceCode)。功能和使用和OutputStream 極其類似。
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); // 定位文件位置 Writer out =new FileWriter(f); //創建字符輸出流連接到文件 String str="哈哈哈哈哈哈"; out.write(str); //寫操作 out.close(); //關閉流 }
加入緩沖流 提高效率。
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); // 定位文件位置 Writer out =new FileWriter(f); //創建字符輸出流連接到文件 BufferedWriter bfw =new BufferedWriter(out); //創建緩沖流 String str="哈哈哈哈哈哈"; bfw.write(str); //寫操作 bfw.close(); //關閉緩沖流 out.close(); //關閉流 }
四、字符流與字節流轉換
轉換流的特點:
(1)其是字符流和字節流之間的橋梁
(2)可對讀取到的字節數據經過指定編碼轉換成字符
(3)可對讀取到的字符數據經過指定編碼轉換成字節
何時使用轉換流?
當字節和字符之間有轉換動作時;
流操作的數據需要編碼或解碼時。
具體的對象體現:
InputStreamReader:字節到字符的橋梁
OutputStreamWriter:字符到字節的橋梁
這兩個流對象是字符體系中的成員,它們有轉換作用,本身又是字符流,所以在構造的時候需要傳入字節流對象進來。
/**
* 將字節輸出流轉化為字符輸出流
* */
public static void main(String[] args) throws IOException { File file=new File("aaa.txt"); Writer out=new OutputStreamWriter(new FileOutputStream(file)); out.write("hello"); out.close(); }
/** * 將字節輸入流變為字符輸入流 * */ public static void main(String[] args) throws IOException { File file=new File("aaa.txt"); Reader read=new InputStreamReader(new FileInputStream(file)); char[] b=new char[100]; int len=read.read(b); System.out.println(new String(b,0,len)); read.close(); }