Java IO編程——字符流與位元組流
- 2019 年 11 月 2 日
- 筆記
在java.io包裏面File類是唯一 一個與文件本身有關的程序處理類,但是File只能夠操作文件本身而不能夠操作文件的內容,或者說在實際的開發之中IO操作的核心意義在於:輸入與輸出操作。而對於程序而言,輸入與輸出可能來自於不同的環境,例如:通過電腦連接服務器上進行瀏覽的時候,實際上此時客戶端發出了一個信息,而後服務器接收到此信息之後進行回應處理。

對於服務器或者是客戶端而言實際上傳遞的就是一種數據流的處理形式,而所謂的數據流指的就是位元組數據。而對於這種流的處理形式在java.io包裏面提供有兩類支持:
·位元組處理流:OutputStream(輸出位元組流)、InputStream(輸入位元組流);
·字符處理流:Writer(輸出字符流)、Reader(輸入字符流);
所有的流操作都應該採用如下統一的步驟進行,下面以文件處理的流程為例:
·如果現在要進行的是文件的讀寫操作,則一定要通過File類找到一個文件路徑;
·通過位元組流或字符流的子類為父類對象實例化;
·利用位元組流或字符流中的方法實現數據的輸入與輸出操作;
·流的操作屬於資源操作,資源操作必須進行關閉處理;
1. 位元組輸出流:OutputStream
位元組的數據是以byte類型為主實現的操作,在進行位元組內容輸出的時候可以使用OutputStream類完成,這個類的基本定義如下:
public abstract class OutputStream extends Object implements Closeable, Flushable
首先可以發現這個類實現了兩個接口,於是基本的對應關係如下:
|
Closeable: |
Flushable: |
|
public interface Closeable extends AutoCloseable {public void close() throws Exception;} |
public interface Flushable{public void flush() throws IOException;} |

OutputStream類定義的是一個公共的輸出操作標準,而在這個操作標準裏面一共定義有三個內容輸出的方法。
|
No |
方法名稱 |
類型 |
描述 |
|
01 |
public abstract void write(int b) throws IOException |
普通 |
輸出單個位元組數據 |
|
02 |
public void write(byte[] b) throws IOException |
普通 |
輸出一組位元組數據 |
|
03 |
public void write(byte[] b,int off,int len) throws IOException |
普通 |
輸出部分位元組數據 |
但是需要注意的一個核心問題在於:OutputStream類畢竟是一個抽象類,而這個抽象類如果要想獲得實例化對象,按照傳統的認識應該通過子類實例的向上轉完成,如果說現在要進行的是文件處理操作,則可以使用FileOutputStream子類:

因為最終都需要發生向上轉型的處理關係,所以對於此時的FileOutputStream子類核心的關注點就可以放在構造方法:
·【覆蓋】構造方法:public FileOutputStream(File file) throws FileNotFoundException;
·【追加】構造方法:public FileOutputStream(File file,boolean append) throws FileNotFoundException;
範例:使用OutputStream類實現內容的輸出
1 import java.io.File; 2 import java.io.FileOutputStream; 3 import java.io.OutputStream; 4 public class JavaAPIDemo { 5 public static void main(String[] args) throws Exception { 6 File file = new File("D:" + File.separator + "hello" + 7 File.separator + "mldn.txt"); // 1、指定要操作的文件的路徑 8 if (!file.getParentFile().exists()) { // 文件不存在 9 file.getParentFile().mkdirs() ; // 創建父目錄 10 } 11 OutputStream output = new FileOutputStream(file) ; // 2、通過子類實例化 12 String str = "www.mldn.cn" ; // 要輸出的文件內容 13 output.write(str.getBytes()); // 3、將字符串變為位元組數組並輸出 14 output.close(); // 4、關閉資源 15 } 16 }
JavaAPIDemo
本程序是採用了最為標準的形式實現了輸出的操作處理,並且在整體的處理之中,只是創建了文件的父目錄,但是並沒有創建文件,而在執行後會發現文件可以自動幫助用戶創建。另外需要提醒的是,由於OutputStream子類也屬於AutoCloseable接口子類,所以對於close()方法也可以簡化使用。
範例:自動關閉處理
1 import java.io.File; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 import java.io.OutputStream; 5 public class JavaAPIDemo { 6 public static void main(String[] args) throws Exception { 7 File file = new File("D:" + File.separator + "hello" + File.separator + "pluto.txt"); // 1、指定要操作的文件的路徑 8 if (!file.getParentFile().exists()) { // 文件不存在 9 file.getParentFile().mkdirs(); // 創建父目錄 10 } 11 try (OutputStream output = new FileOutputStream(file, true)) {//true表示追加數據 12 String str = "www.cnblogs.comrn"; // 要輸出的文件內容 13 output.write(str.getBytes()); // 3、將字符串變為位元組數組並輸出 14 } catch (IOException e) { 15 e.printStackTrace(); 16 } 17 } 18 }
JavaAPIDemo
是否使用自動的關閉取決於項目的整體結構,另外還需要提醒的是,整個的程序裏面最終是輸出了一組的位元組數據,但是千萬不要忘記了,OutputStream類之中定義的輸出方法一共有三個。
2. 位元組輸入流:InputStream
與OutputStream類對應的一個流就是位元組輸入流,InputStream類主要實現的就是位元組數據讀取,該類定義如下:
public abstract class InputStream extends Object implements Closeable
在InputStream類裏面定義有如下的幾個核心方法:
|
No |
方法名稱 |
類型 |
描述 |
|
01 |
public abstract int read() throws IOException |
普通 |
讀取單個位元組數據,如果現在已經讀取到底,則返回-1 |
|
02 |
public int read(byte[] b) throws IOException |
普通 |
讀取一組位元組數據,返回的是讀取的個數,如果沒有數據已經讀取到底則返回-1 |
|
03 |
public int read(byte[] b,int off,int len) throws IOException |
普通 |
讀取一組位元組數據(只佔數組的部分) |

InputStream類屬於一個抽象類,這時應該依靠它的子類來實例化對象,如果要從文件讀取一定使用FileInputStream子類,對於子類而言只關心父類對象實例化。
構造方法:public FileInputStream(File file) throws FileNotFoundException;
1 import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.InputStream; 4 public class JavaAPIDemo { 5 public static void main(String[] args) throws Exception { 6 File file = new File("D:" + File.separator + "hello" + File.separator + "mldn.txt"); 7 InputStream input = new FileInputStream(file) ; 8 byte data [] = new byte [1024] ; // 開闢一個緩衝區讀取數據 9 int len = input.read(data) ; // 讀取數據,數據全部保存在位元組數組之中,返回讀取個數,如果mldn.txt文件中的長度大於1024則只會讀取到1024位元組長度的信息 10 System.out.println("【" + new String(data, 0, len) + "】"); 11 input.close(); 12 } 13 }
JavaAPIDemo
對於位元組輸入流裏面最為麻煩的問題就在於:使用read()方法讀取的時候只能夠以位元組數組為主進行接收。
特別需要注意的是從JDK1.9開始在InputStream類裏面增加了一個新的方法:public byte[] readAllBytes() throws IOException;
1 import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.InputStream; 4 public class JavaAPIDemo { 5 public static void main(String[] args) throws Exception { 6 File file = new File("D:" + File.separator + "hello" + File.separator + "mldn.txt"); 7 InputStream input = new FileInputStream(file) ; 8 byte data [] = input.readAllBytes() ; // 讀取全部數據 9 System.out.println("【" + new String(data) + "】"); 10 input.close(); 11 } 12 }
JavaAPIDemo
如果現在要讀取的內容很大很大的時候,那麼這種讀取直接使程序崩潰。如果要使用盡量不要超過10KB。
3. 字符輸出流:Writer
使用OutputStream位元組輸出流進行數據輸出的時候使用的都是位元組類型的數據,而很多的情況下字符串的輸出是比較方便的,所以對於java.io包而言,在JDK1.1的時候又推出了字符輸出流:Writer,這個類的定義如下:
public abstract class Writer extends Object implements Appendable, Closeable, Flushable

在Writer類裏面提供有許多的輸出操作方法,重點來看兩個:
·輸出字符數組:public void write(char[] cbuf) throws IOException;
·輸出字符串:public void write(String str) throws IOException;
範例:使用Writer輸出
1 import java.io.File; 2 import java.io.FileWriter; 3 import java.io.Writer; 4 public class JavaAPIDemo { 5 public static void main(String[] args) throws Exception { 6 File file = new File("D:" + File.separator + "hello" + File.separator + "mldn.txt"); 7 if (!file.getParentFile().exists()) { 8 file.getParentFile().mkdirs(); // 父目錄必須存在 9 } 10 Writer out = new FileWriter(file) ; 11 String str = "www.mldn.cn" ; 12 out.write(str); 13 out.close(); 14 } 15 }
JavaAPIDemo
JavaAPIDemo使用Writer輸出的最大優勢在於可以直接利用字符串完成。Writer是字符流,字符處理的優勢在於中文數據上。
4. 字符輸入流:Reader
Reader是實現字符輸入流的一種類型,其本身屬於一個抽象類,這個類的定義如下:
public abstract class Reader extends Object implements Readable, Closeable

Reader類裏面並沒有像Writer類一樣提供有整個字符串的輸入處理操作,只能夠利用字符數據來實現接收:
·接收數據:public int read(char[] cbuf) throws IOException;
範例:實現數據讀取
1 import java.io.File; 2 import java.io.FileReader; 3 import java.io.Reader; 4 public class JavaAPIDemo { 5 public static void main(String[] args) throws Exception { 6 File file = new File("D:" + File.separator + "hello" + File.separator + "mldn.txt"); 7 if (file.exists()) { // 文件存在則進行讀取 8 Reader in = new FileReader(file) ; 9 char data[] = new char[1024]; 10 int len = in.read(data) ; 11 System.out.println("讀取內容:" + new String(data,0,len)); 12 in.close(); 13 } 14 } 15 }
JavaAPIDemo
字符流讀取的時候只能夠按照數組的形式來實現處理操作。
5. 位元組流與字符流的區別
現在通過一系列的分析已經可以清楚位元組流與字符流的基本操作了,但是對於這兩類流依然是存在有區別的,重點來分析一下輸出的處理操作。在使用OutputStream和Writer輸出的最後發現都使用了close()方法進行了關閉處理。
在使用OutputStream類輸出的時候如果沒有使用close()方法關閉輸出流發現內容依然可以實現正常的輸出,但是如果在使用Writer的時候沒有使用close()方法關閉輸出流,那麼這個時候內容將無法進行輸出,因為Writer使用到了緩衝區,當使用了close()方法的時候實際上會出現有強制刷新緩衝區的情況,所以這個時候會將內容進行輸出,如果沒有關閉,那麼將無法進行輸出操作,所以此時如果在不關閉的情況下要想將全部的內容輸出可以使用flush()方法強制清空。
範例:使用Writer並強制性清空
1 import java.io.File; 2 import java.io.FileWriter; 3 import java.io.Writer; 4 public class JavaAPIDemo { 5 public static void main(String[] args) throws Exception { 6 File file = new File("D:" + File.separator + "hello" + File.separator + "mldn.txt"); 7 if (!file.getParentFile().exists()) { 8 file.getParentFile().mkdirs(); // 父目錄必須存在 9 } 10 Writer out = new FileWriter(file) ; 11 String str = "www.mldn.cn" ; 12 out.write(str); 13 out.flush(); // 強制性刷新 14 } 15 }
JavaAPIDemo
問題:可以修改代碼使out.flush()存在和不存在的區別?查看這兩者有什麼區別?
注意:以上例子中的out流不可以關閉,否則會自動flush()便達不到我們想要的結果
位元組流在進行處理的時候並不會使用到緩衝區,而字符流會使用到緩衝區。另外使用緩衝區的字符流更加適合於進行中文數據的處理,所以在日後的程序開發之中,如果要涉及到包含有中文信息的輸出一般都會使用字符流處理,但是從另外一方面來講,位元組流和字符流的基本處理形式是相似的,由於IO很多情況下都是進行數據的傳輸使用(二進制)所以本次的講解將以位元組流為主。


