一、什麼是io流
JavaIO流,是一種計算機用語。主要是用於處理數據的傳輸。
——百度百科
流是指數據的流動,io流就是輸入輸出的流動。
在java中對數據的操作是用流的方式來實現的,數據不可能一瞬間就全部地從一個設備傳到另一個設備,所以只能採用一點一點流動的方式。
輸入流就是將其他地方的數據讀取到程序中,輸出流就是將程序中的數據寫入到其他地方。
二、io流的分類
1.四個抽象類
在java.io中提供了最重要的四個抽象類,InputStream(字節輸入流),OutputStream(字節輸出流),Reader(字符輸入流),Writer(字符輸出流)
一字符等於兩字節
根據輸出的數據單位,可以分爲兩類,字節流和字符流
還可以根據輸入或輸出,分爲輸入流和輸出流
根據實現功能的不同,還可以分爲節點流和處理流
節點流就是,從一個特定的節點讀取或者寫入數據。一看到節點就感覺好深奧,其實說白了就是從一個地方讀取或寫入數據,而這個地方可以是數據庫,控制檯,文件等。
處理流就是,代理節點流,實現一些其他的功能。像BufferedWriter,如果是普通的FileWriter,在執行寫操作時就就只能一個字符一個字符來寫,如果再套上一個BufferedWriter,就會有緩存的功能,寫的操作就會更加的快速了。
2.各種實現類
可能會有所遺漏,更加詳細的可以查看java官方的文檔。
三、如何使用
以FileInputStream和FileOutputStream爲例
1.代碼
package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* @author xxj
* io流測試
*/
public class IOTest {
public static void main(String[] args) {
//先打開一個文件
File file= new File("C:\\Users\\Administrator\\Desktop\\test.txt");
try{
//先寫入數據
FileOutputStream fileOutputStream=new FileOutputStream(file);
fileOutputStream.write("ABCD".getBytes());
fileOutputStream.close();
//讀取數據
FileInputStream fileInputStream=new FileInputStream(file);
int i= 0;
while ((i=fileInputStream.read())!=-1){
System.out.println("通過fileInputStream讀取的數據:"+i);
System.out.println("通過fileInputStream讀取的數據:"+(char)i);
System.out.println("------");
}
fileInputStream.close();
}catch (Exception e){}
}
}
2.輸出結果
因爲我加了(char)所以自動做了ASCII碼錶的轉換
我的桌面也生成了test.txt文件,有興趣的拿代碼去跑一下吧。
3.解釋
可能有人會問,你這不對啊,你不是FileInputStream和FileOutputStream嗎,怎麼剛好每次讀取都是一個字母呢,不是一個字節嗎?
一個字節是八位,再看一下ASCII碼錶,裏面已經有上百個符號或字母了,肯定不能只用4bit來表示啦,最少也要用8bit,8bit最高可以表示256,所以這裏直接讀取就是單個字母來讀取的。
再以FileReader和FileWriter爲例
4.代碼
public class IOTest {
public static void main(String[] args) {
try{
//先寫入
File file=new File("C:\\Users\\Administrator\\Desktop\\writer.txt");
FileWriter fileWriter=new FileWriter(file);
fileWriter.write("ABC一二三");
fileWriter.close();
//讀取
FileReader fileReader=new FileReader(file);
int i=0;
while ((i=fileReader.read())!=-1){
System.out.println("通過reader讀取的數據:"+i);
System.out.println("通過reader讀取的數據:"+(char)i);
System.out.println("------");
}
fileReader.close();
}catch (Exception e){}
}
}
5. 輸出結果
6.解釋
我有個疑問,FileReader爲什麼可以讀字母,跟FileInputStream一樣,那爲什麼它會剛好可以只讀一箇中文又只讀到一個字母呢?
19968=0x4e00
writer.txt的16進制文件爲
4142 43 e4b8 80e4 ba8c e4b8 89
根本沒有4e00,那麼我們再轉化成二進制
(一)19968=100111000000000
(二)20108=100111010001100
(三)19977=100111000001001
0xe4b880e4ba8ce4b889=111001001011100010000000111001001011101010001100111001001011100010001001
我使用了String.replace()方法,發現e4b8 80e4 ba8c e4b8 89中並沒有包含一二三的二進制。
這裏就涉及到編碼了,常用的支持中文的編碼表是UTF-8,於是我將一二三拉進去查看進制轉換
這。真的是一套一套的。
到了這裏,也不是很清楚究竟是怎麼確認只讀一個字節還是讀一個字符
四、各種io流的特點
不同的io流使用起來是差不多的,只是細節上有些不同,具體的可以查查百度或者直接看每個實現類的源碼,畢竟它們都是分別繼承了InputStream(字節輸入流),OutputStream(字節輸出流),Reader(字符輸入流),Writer(字符輸出流),其中處理流是根據Filter···stream間接繼承的。
我稍微看了一下,想FileOutputStream最後的寫操作是帶有native修飾符的,又要查漏補缺了。
private native void writeBytes(byte b[], int off, int len, boolean append)
throws IOException;
字節流和字符流的區別
- 字節流一次只能操作一個字節,字符流可以操作兩個字節
- 字節流能夠處理任何文件類型,而字符流只能處理文本類型的文件
1.節點流
File流
字節:FileInputStream,FileOutputStream
字符:FileReader,FileWriter
特點
這些都是需要先創建一個File對象,然後才能使用的,而讀取或寫入操作都是在創建的File對象上做的。
只有一個區別,前兩個是字節流,後兩個是字符流,如果需要讀取或寫入中文,就需要使用後兩個。
Piped流
字節:PipedInputStream,PipedOutStream,
字符:PipedReader,PipedWriter
一般是多線程之間進行通信時使用的
特點
在使用這些流時,相比與File流,就不用先創建一個File對象,只需要跟普通的流一樣創建即可,但是在管道(Piped)相互通信前,需要使用connect方法將兩個管道連接起來
Piped流不需要使用文件作爲數據的載體。
字節/字符數組流
字節:ByteArrayInputStream,ByteArrayOutputStream
字符:CharArrayReader,CharArrayWriter
這四個流內部都會有一個數組,使用時是先向數組中存入數據,然後在從數組中讀取或將數組的數據寫入到其他地方。
特點
Byte開頭的,就是隻能讀取或寫入字節。ByteArrayInputStream在創建時需要向構造方法傳遞一個參數(字節數組)ByteArrayOutputStream則在使用時,需要調用writeTo(某個流)這兩個流可以幫助我們先將數據存儲到緩存區,然後讀取寫入更加快速,開闢的緩存區是用來存儲字節數組的
CharArrayReader,CharArrayWriter和ByteArrayInputStream,ByteArrayOutputStream類似,只不過Char開頭的是讀寫字符,而Byte是讀寫字節,用法都一樣的。
2.處理流
處理流在使用的時候,是需要套上一個節點流,然後使用方法跟使用節點流差不多。
Buffered緩衝流
字節:BufferedInputStream,BufferedOutputStream
字符:BufferedReader,BufferedWriter
這四個流跟字節/字符流類似,它們內部也有一個數組作爲緩存區存在。但是,字節/字符數組流是將數據都緩存到數組中,並不會去刷新這個數組,想讀取或寫入都是從這個數組出發的;而Buffered緩衝流,這會有一個不會二次改變的數組(可以在構造方法中傳入size)一旦這個數組存滿了,就會將數組的數據全部讀出或寫入到其他地方,然後刷新數組。
字節/字符數組流建議一次性用完就關閉。
特點
在套用了其他節點流之後,使用的方法和節點流的使用方式差不多,但是在Buffered緩衝流寫入時還需要使用一個flush()方法,將緩存內部的數據全部寫入,並且刷新緩存。
轉化流
字符:InputStreamReader,OutputStreamWriter
特點
就是能夠將字節流轉換成字符流,在創建時,需要向構造方法傳遞一個字節流和字符集(默認爲GBK)使用的方法跟字符流的使用方法差不多。
數據流
字節:DataInputStream,DataOutputStream
特點
數據流是能夠將數據按照不同的類型寫入,讀取時也要按照相應的類型讀取。類似於寫入時,將數據裝入一個個不同類型的盒子了,讀取時要按照盒子的類型並且按照寫入的順序來讀取。
打印流
字節:PrintStream
字符:PrintWriter
特點
這兩個流並沒有對應的輸入流,使用這兩個的方法跟其他處理流差不多。在套上一個節點流後,就可以使用print()或println()直接寫入數據。
有沒有覺得這兩個方法很熟悉,平時我們使用System.out.println()這個方法的內部其實也是使用PrintStream來實現的。
對象流
字節:ObjectInputStream,ObjectOutputStream
特點
這兩個流可以做一件事,就是將我們要寫入或讀取的數據序列化,序列化就是將對象轉化成二進制數據。要使用這兩個流,第一步也是要套上一個節點流,第二步就是在要序列化的類上實現Serializable接口,剩下的使用方法就跟普通的節點流差不多了。
五、總結
這些流真的是多,總結一下使用場景吧。
寫入或讀取都是文本類型還有中文,就用字符流。
寫入或讀取其他雜七雜八的文件類型且沒有中文,就用字節流。
要考慮線程通信,就用Piped流;要操作文件,就用File流;要將數據存在內存中,就用字節/字符數組流,沒有性能和功能要求,用這三個就夠了。
想要提高讀寫性能,就套個Buffered緩衝流;想要對規定數據格式,就套個數據流;想要使用方便或換行,就套個打印流;想要對對象序列化,就套個對象流。
——————————————————————————————
如果本文章內容有問題,請直接評論或者私信我。
未經允許,不得轉載!