Java中的字節,字符輸出流與字節和字符輸入流的簡單理解
我先解釋一下什麼叫IO流:
- I:指的是InputStream,這是一個抽象類,最常用的子類是FileInputStream
- O:值得是OutputStream,這也是一個抽象類,最常用的子類是OutputStream
- 流:由於在進行文件操作的時候大多數是用的byte數據,這些數據並不是一次性寫入(讀取),而是像水龍頭那樣慢慢的流(想象一下你接水的場景)
廢話還是不多bb,先來一份簡單的代碼:
File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if (!file.exists()){ file.createNewFile(); }
其中File.separator
指的是當前系統的默認分隔符,這樣寫的原因是可以保證Java文件在Windows系統運行時和Linux系統運行時都不會出錯
這段代碼也很簡單,主要就是創建一個文件。
當然,這都不是重點,重點在下面
字節輸出流OutputStream
對於Output Stream類來說,它本身定義的是一個抽象類,按照抽象類的原則來講,需要定義抽象類的子類,而我們要執行的是文件操作,則可以使用FileOutputStream子類來完成。而我們最關心的還是子類中的構造方法
方法 | 描述 |
---|---|
public FileOutputStream(File file) throws FileNotFoundException | 實例化FileOutputStream,主要用於新建數據 |
public FileOutputStream(File file,boolean append) throws FileNotFoundException | 實例化FileOutputStream,主要用於追加數據 |
我們在實例化OutputStream對象之後肯定要進行輸出操作。在OutputStream類中定義瞭3個輸出方法。例如:
方法 | 描述 |
---|---|
public abstract void write(int b) throws IOException | 輸出單個字節數據 |
public void write(byte[] b) throws IOException | 輸出一組字節數據 |
public abstract void write(byte[] b,int off,int len) throws IOException | 輸出部分字節數據 |
可能大傢在看表的時候已經發現瞭,都是byte類型的數據。
使用OutputStream向文件中輸出數據。
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if (!file.exists()){ file.createNewFile(); } OutputStream output=new FileOutputStream(file);// 實例化父類 String data="Hello World!"; output.write(data.getBytes()); output.close(); } }
可以發現,在文件輸出的過程中,如果要輸出的文件和目錄不存在那麼會覆蓋掉原有的內容,咋辦呢?別忘瞭我們還有一個構造方法專門是為瞭追加數據的:
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if (!file.exists()){ file.createNewFile(); } OutputStream output=new FileOutputStream(file,true);// 追加數據 String data="Hello World!"; output.write(data.getBytes()); output.close(); } }
執行一遍會發現,會自動的把數據附加在已有的數據後面。
我們在來看看另一種類似的流
字符輸出流
看標題,字節和字符就差一個字,但是,熟悉Java數據基本類型的都知道。這倆貨一個是byte,一個是String。那麼我們在對文件進行輸出操作的時候,就可以把需要輸出的內容定義成String類型而不是byte字節型;
同樣,Writer也是一個抽象類,當我們用於文件操作的時候,常用的子類就是FileWriter。我們來看看Writer類的常用方法:
方法 | 描述 |
---|---|
public abstract void close() throws IOException | 關閉輸出流 |
public void write(String str) throws IOException | 將字符串輸出 |
public void write(char[] cbuf) throws IOException | 將字符數組輸出 |
public abstract void flush() throws IOException | 強制性清空內存 |
還是不多bb,上代碼,就知道啥樣子瞭:
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if (!file.exists()){ file.createNewFile(); } Writer out=new FileWriter(file); String data="Hello World!"; out.write(data); out.close(); } }
瞭解瞭輸出流,我們再來看看輸入流;
Java中的輸入流有兩種,一種是InputStream,另一種就是Reader。看這名字就知道,md,可能又是一種簡單的一種難的。沒錯,你猜對瞭;
字節輸入流InputStream
同樣,這貨也是一個抽象類,用於文件操作的也是他的子類FileInputStream,當然也有幾個方法用於操作文件:
方法 | 描述 |
---|---|
public abstract int read() throws IOException | 讀取單個字節數據,每次執行read()方法都會讀取一個數據源的指定數據,如果已經讀到瞭結尾,則會返回-1 |
public int read(byte[] b) throws IOException | 讀取多個字節數據,如果要讀取的數據小於byte的數據,這個時候read()方法的返回值int返回的是數據個數,如果現在開辟的字節數組小於讀取的長度,且數據已經讀取完瞭。則這個時候返回的是-1 |
public int read(byte[] int off,int len) throws IOException | 讀取指定多個字節數據 |
我們還是看看讀取內容,具體代碼怎麼去實現它:
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if(file.exists()){ InputStream input=new FileInputStream(file); byte data[] = new byte[1024];// 開辟一個1024長度的byte數組 int len=input,read(data); input.close(); System.out.println("讀取的內容:"+new String(data,0,lem)); } } }
上述代碼簡明的表達瞭讀取文件的全部內容的邏輯,但是想象一下,單個單個的讀取怎麼做呢?
這時候需要一點以前的知識瞭,看看代碼:
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if(file.exists()){ InputStream input=new FileInputStream(file); byte data[] = new data[1024]; int foot=0;// 數組的索引初始值 int temp=0;// 待會自己看是啥作用 while((temp=input.read())!=-1){ data[foot++]=(byte) temp; input.close(); System.out.println("讀取到的數據是:"+new String(data,0,foot)); } } } }
瞭解瞭字節輸入流,是不是還得瞭解一下字符輸入流。來吧,也別愣著瞭,碼代碼唄;
字符輸入流Reader
那些啥抽象啊,子類啊啥的我都不說瞭,反正類似,自己慢慢琢磨琢磨。
看看有哪些方法:
方法 | 描述 |
---|---|
public abstract void close() throws IOException | 關閉流 |
public int read() throws IOException | 讀取單個字符 |
public int read(char[] cbuf) throws IOException | 將內容讀到字符數組中,返回讀入的長度 |
有一點和上面的不一樣:
雖然Writer類中提供瞭輸出字符串數據的操作方法,但是在Reader類中並沒有這樣的定義。之所以會這個樣子,完全是因為在使用OutputStream輸出數據時,其程序可以輸出的大小一定是程序可以承受的數據大小,如果在使用InputStream讀取時,可能被讀取的數據灰常大,一次性全部讀取的話可能會問題,於是就隻有一個一個的讀取
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } Reader in=new FileReader(file); char data[] = new char[1024]; int len=in.read(data); in.close(); System.out.println("讀取的內容:"+new String(data,0,len)); } }
代碼寫瞭這麼一大堆,我們最後再看看一個問題;
字節流和字符流的區別
通過以上的代碼演示我們知道瞭,字節流和字符流都有類似的功能,那麼在開發的過程中具體使用哪一種呢?
他們的區別在於:
字節流在進行IO操作時,直接針對的時操作的數據終端(如文件),而字符流操作時不是直接針對於終端,而是針對於緩存區(理解為內存)的操作,而後由緩存區來操作終端(如文件),這屬於間接操作,按照這樣的方式,如果在使用字節流時不關閉最後的輸出操作,也可以將所有的內容進行輸出,而使用字符流時如果不關閉,則意味著緩沖區的內容不會被輸出,當然,這個時候可以由用戶自己調用flush()方法去強制性的手動清空 例如:
import java.io.*; public class test { public static void main(String[] args) throws Exception{ File file=new File("e:"+File.separator+"JavaLearn"+File.separator+"EleventhDemo"+File.separator+"1.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); System.out.println("父級目錄創建成功"); } if (!file.exists()){ file.createNewFile(); } Writer out=new FileWriter(file); String data="Hello World!"; out.write(data); out.flush(); } }
總結一下,字節流和字符流的主要區別:
- 字節流沒有使用到緩沖區,而字符流使用瞭;
- 處理各種數據都可以通過字節流完成,而在處理中午的時候使用字符流會更方便;
最後,留一個思考題給有興趣的小夥伴。
現有一個要求,按照DOS系統的文件拷貝命令,由初始化參數輸入源文件和拷貝文件的路徑,而後執行操作。
提示:本程序直接在主方法中完成,不考慮多餘的方法和類的設計。考慮大文件的情況(500MB以上)
我把思路也貼給大傢:
方案一:將要復制的文件全部讀取到內存中,而後將所有的內容一次性輸出到目標文件;
方案二:采用邊讀邊寫的方式一點一點的進行文件的復制。
總結
本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!