Java IO程式設計——字元流與位元組流
在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.com\r\n"; // 要輸出的檔案內容 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很多情況下都是進行資料的傳輸使用(二進位制)所以本次的講解將以位元組流為主。
&n