Java位元組流與字元流的區別詳解
先來看一下流的概念:
在程式中所有的資料都是以流的方式進行傳輸或儲存的,程式需要資料的時候要使用輸入流讀取資料,而當程式需要將一些資料儲存起來的時候,就要使用輸出流完成。
程式中的輸入輸出都是以流的形式儲存的,流中儲存的實際上全都是位元組檔案。
位元組流與字元流
在Java.io包中操作檔案內容的主要有兩大類:位元組流、字元流,兩類都分為輸入和輸出操作。在位元組流中輸出資料主要是使用OutputStream完成,輸入使的是InputStream,在字元流中輸出主要是使用Writer類完成,輸入流主要使用Reader類完成。(這四個都是抽象類)
java中提供了專用於輸入輸出功能的包Java.io,其中包括:
InputStream,OutputStream,Reader,Writer
InputStream 和OutputStream,兩個是為位元組流設計的,主要用來處理位元組或二進位制物件,
Reader和 Writer.兩個是為字元流(一個字元佔兩個位元組)設計的,主要用來處理字元或字串.
字元流處理的單元為2個位元組的Unicode字元,分別操作字元、字元陣列或字串,而位元組流處理單元為1個位元組,操作位元組和位元組陣列。所以字元流是由Java虛擬機器將位元組轉化為2個位元組的Unicode字元為單位的字元而成的,所以它對多國語言支援性比較好!如果是音訊檔案、圖片、歌曲,就用位元組流好點,如果是關係到中文(文字)的,用字元流好點
所有檔案的儲存是都是位元組(byte)的儲存,在磁碟上保留的並不是檔案的字元而是先把字元編碼成位元組,再儲存這些位元組到磁碟。在讀取檔案(特別是文字檔案)時,也是一個位元組一個位元組地讀取以形成位元組序列
位元組流可用於任何型別的物件,包括二進位制物件,而字元流只能處理字元或者字串; 2. 位元組流提供了處理任何型別的IO操作的功能,但它不能直接處理Unicode字元,而字元流就可以
位元組流是最基本的,所有的InputStrem和OutputStream的子類都是,主要用在處理二進位制資料,它是按位元組來處理的 但實際中很多的資料是文字,又提出了字元流的概念,它是按虛擬機器的encode來處理,也就是要進行字符集的轉化 這兩個之間通過 InputStreamReader,OutputStreamWriter來關聯,實際上是通過byte[]和String來關聯 在實際開發中出現的漢字問題實際上都是在字元流和位元組流之間轉化不統一而造成的
==================我們還可以看到:============
Reader類的read()方法返回型別為int :作為整數讀取的字元(佔兩個位元組共16位),範圍在 0 到 65535
inputStream的read()雖然也返回int,但由於此類是面向位元組流的,一個位元組佔8個位,所以返回 0 到 255 範圍內的 int 位元組值。如果因為已經到達流末尾而沒有可用的位元組,則返回值 -1。因此對於不能用0-255來表示的值就得用字元流來讀取!比如說漢字.
操作流程
在Java中IO操作也是有相應步驟的,以檔案操作為例,主要的操作流程如下:
1 使用File類開啟一個檔案
2 通過位元組流或字元流的子類,指定輸出的位置
3 進行讀/寫操作
4 關閉輸入/輸出
IO操作屬於資源操作,一定要記得關閉
位元組流
位元組流主要是操作byte型別資料,以byte陣列為準,主要操作類就是OutputStream、InputStream
位元組輸出流:OutputStream
OutputStream是整個IO包中位元組輸出流的最大父類,此類的定義如下:
public abstract class OutputStream extends Object implements Closeable,Flushable
從以上的定義可以發現,此類是一個抽象類,如果想要使用此類的話,則首先必須通過子類例項化物件,那麼如果現在要操作的是一個檔案,則可以使用:FileOutputStream類。通過向上轉型之後,可以為OutputStream例項化
Closeable表示可以關閉的操作,因為程式執行到最後肯定要關閉
Flushable:表示重新整理,清空記憶體中的資料
FileOutputStream類的構造方法如下:
public FileOutputStream(File file)throws FileNotFoundException
寫資料:
1import java.io.File;2import java.io.FileOutputStream;3import java.io.IOException;4import java.io.OutputStream;56publicclass Test11 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 OutputStream out=new FileOutputStream(f);//如果檔案不存在會自動建立10 String str="Hello World";11byte[] b=str.getBytes();12 out.write(b);//因為是位元組流,所以要轉化成位元組陣列進行輸出13 out.close();14 }15 }
也可以一個位元組一個位元組進行輸出,如下:
1import java.io.File;2import java.io.FileOutputStream;3import java.io.IOException;4import java.io.OutputStream;56publicclass Test11 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 OutputStream out=new FileOutputStream(f);//如果檔案不存在會自動建立10 String str="Hello World";11byte[] b=str.getBytes();12for(int i=0;i<b.length;i++){13 out.write(b[i]);14 }15 out.close();16 }17 }
以上輸出只會進行覆蓋,如果要追加的話,請看FileOutputStream類的另一個構造方法:
public FileOutputStream(File file,boolean append)throws FileNotFoundException
在構造方法中,如果將append的值設定為true,則表示在檔案的末尾追加內容。
1import java.io.File;2import java.io.FileOutputStream;3import java.io.IOException;4import java.io.OutputStream;56publicclass Test11 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 OutputStream out=new FileOutputStream(f,true);//追加內容10 String str="\r\nHello World";11byte[] b=str.getBytes();12for(int i=0;i<b.length;i++){13 out.write(b[i]);14 }15 out.close();16 }17 }
檔案中換行為:\r\n
位元組輸入流:InputStream
既然程式可以向檔案中寫入內容,則就可以通過InputStream從檔案中把內容讀取進來,首先來看InputStream類的定義:
public abstract class InputStream extends Object implements Closeable
與OutputStream類一樣,InputStream本身也是一個抽象類,必須依靠其子類,如果現在是從檔案中讀取,就用FileInputStream來實現。
觀察FileInputStream類的構造方法:
public FileInputStream(File file)throws FileNotFoundException
讀檔案:
1import java.io.File;2import java.io.FileInputStream;3import java.io.IOException;4import java.io.InputStream;56publicclass Test12 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 InputStream in=new FileInputStream(f);10byte[] b=newbyte[1024];11int len=in.read(b);12 in.close();13 System.out.println(new String(b,0,len));14 }15 }
但以上方法是有問題的,用不用開闢這麼大的一個位元組陣列,明顯是浪費嘛,我們可以根據檔案的大小來定義位元組陣列的大小,File類中的方法:public long length()
1import java.io.File;2import java.io.FileInputStream;3import java.io.IOException;4import java.io.InputStream;56publicclass Test13 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 InputStream in=new FileInputStream(f);10byte[] b=newbyte[(int) f.length()];11 in.read(b);12 in.close();13 System.out.println(new String(b));14 }15 }
我們換種方式,一個位元組一個位元組讀入~
1import java.io.File;2import java.io.FileInputStream;3import java.io.IOException;4import java.io.InputStream;56publicclass Test14 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 InputStream in=new FileInputStream(f);10byte[] b=newbyte[(int) f.length()];11for(int i=0;i<b.length;i++){12 b[i]=(byte) in.read();13 }14 in.close();15 System.out.println(new String(b));16 }17 }
但以上情況只適合知道輸入檔案的大小,不知道的話用如下方法:
1import java.io.File;2import java.io.FileInputStream;3import java.io.IOException;4import java.io.InputStream;56publicclass Test15 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 InputStream in=new FileInputStream(f);10byte[] b=newbyte[1024];11int temp=0;12int len=0;13while((temp=in.read())!=-1){//-1為檔案讀完的標誌14 b[len]=(byte) temp;15 len++;16 }17 in.close();18 System.out.println(new String(b,0,len));19 }20 }
字元流
在程式中一個字元等於兩個位元組,那麼java提供了Reader、Writer兩個專門操作字元流的類。
字元輸出流:Writer
Writer本身是一個字元流的輸出類,此類的定義如下:
public abstract class Writer extends Object implements Appendable,Closeable,Flushable
此類本身也是一個抽象類,如果要使用此類,則肯定要使用其子類,此時如果是向檔案中寫入內容,所以應該使用FileWriter的子類。
FileWriter類的構造方法定義如下:
public FileWriter(File file)throws IOException
字元流的操作比位元組流操作好在一點,就是可以直接輸出字串了,不用再像之前那樣進行轉換操作了。
寫檔案:
1import java.io.File;2import java.io.FileWriter;3import java.io.IOException;4import java.io.Writer;56publicclass Test16 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 Writer out=new FileWriter(f);10 String str="Hello World";11 out.write(str);12 out.close();13 }14 }
在預設情況下再次輸出會覆蓋,追加的方法也是在建構函式上加上追加標記
1import java.io.File;2import java.io.FileWriter;3import java.io.IOException;4import java.io.Writer;56publicclass Test17 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 Writer out=new FileWriter(f,true);//追加10 String str="\r\nHello World";11 out.write(str);12 out.close();13 }14 }
字元輸入流:Reader
Reader是使用字元的方式從檔案中取出資料,Reader類的定義如下:
public abstract class Reader extends Objects implements Readable,Closeable
Reader本身也是抽象類,如果現在要從檔案中讀取內容,則可以直接使用FileReader子類。
FileReader的構造方法定義如下:
public FileReader(File file)throws FileNotFoundException
以字元陣列的形式讀取出資料:
1import java.io.File;2import java.io.FileReader;3import java.io.IOException;4import java.io.Reader;56publicclass Test18 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 Reader input=new FileReader(f);10char[] c=newchar[1024];11int len=input.read(c);12 input.close();13 System.out.println(new String(c,0,len));14 }15 }
也可以用迴圈方式,判斷是否讀到底:
1import java.io.File;2import java.io.FileReader;3import java.io.IOException;4import java.io.Reader;56publicclass Test19 {7publicstaticvoid main(String[] args) throws IOException {8 File f = new File("d:" + File.separator+"test.txt");9 Reader input=new FileReader(f);10char[] c=newchar[1024];11int temp=0;12int len=0;13while((temp=input.read())!=-1){14 c[len]=(char) temp;15 len++;16 }17 input.close();18 System.out.println(new String(c,0,len));19 }20 }
位元組流與字元流的區別
位元組流和字元流使用是非常相似的,那麼除了操作程式碼的不同之外,還有哪些不同呢?
位元組流在操作的時候本身是不會用到緩衝區(記憶體)的,是與檔案本身直接操作的,而字元流在操作的時候是使用到緩衝區的
位元組流在操作檔案時,即使不關閉資源(close方法),檔案也能輸出,但是如果字元流不使用close方法的話,則不會輸出任何內容,說明字元流用的是緩衝區,並且可以使用flush方法強制進行重新整理緩衝區,這時才能在不close的情況下輸出內容
那開發中究竟用位元組流好還是用字元流好呢?
在所有的硬碟上儲存檔案或進行傳輸的時候都是以位元組的方法進行的,包括圖片也是按位元組完成,而字元是隻有在記憶體中才會形成的,所以使用位元組的操作是最多的。
如果要java程式實現一個拷貝功能,應該選用位元組流進行操作(可能拷貝的是圖片),並且採用邊讀邊寫的方式(節省記憶體)。