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的更多內容!

推薦閱讀: