1. 程式人生 > >javaI/O——位元組流與字元流

javaI/O——位元組流與字元流

位元組流與字元流
一、流操作簡介
在java.io包中流分為兩種:
(1)位元組流:InputStream、OutputStream
注意區分我們的InputStream和OutputStream,
OutputStream:這是輸出內容到檔案中(就相當於是我們的Writer寫東西到檔案中)
InputStream:這是從檔案中讀取內容(就相當於是我們的Reader)
(2)字元流:Reader、Writer
對於位元組流和字元流我們下面通過圖片加文字的形式對它們進行解釋:
在這裡插入圖片描述
在這裡插入圖片描述
位元組流與字元流在本質上沒有區別,它們最大的區別就是位元組流是最原始的操作,而字元流是經過了加工處理的,它最開始傳輸的時候也是位元組流,但是在進入終端之前由一個加工廠將字元流加工成了字元流。從終端出去的時候也是需要經過加工程式設計位元組流。
不管我們使用的是位元組流還是字元流,它們的操作基本都是一樣的,以它們對檔案的操作為例:
(1)根據檔案路徑建立File類物件
(2)根據位元組流或字元流的子類例項化父類物件
(3)進行資料的讀取或寫入操作
(4)關閉流(close())
注意:對於I/O操作屬於資源處理,所以我們最後需要關閉。
二、位元組流
1.位元組輸出流
1.1
(1)如果想要通過程式進行內容寫入檔案,可以使用java.io.OutputStream。
OutputStream類的定義結構:
public abstract class OutputStream implements Close,Flushable
(2)從上面的結構我們可以看出OutputStream實現了Closeable,Flushable兩個介面,這兩個介面中的方法分別為:
Closeable:public void close() throws IOException;
Flushable:public void flush() throws IOException;
(3)此外OutputStream中還定義了其他的方法:
a.將給定的位元組陣列內容全部輸出(寫入檔案中):
public void write(byte b[]) throws IOException
b.將部分位元組陣列內容輸出(寫入檔案中):
public void write(byte b[],int off,int len) throws IOException
c.輸出單(寫入檔案中)個位元組:
public abstract void write (int b)throws IOException;
OutputStream是一個抽象類,那麼如果我們想要它的例項化物件只能通過它的子類來實現;但是這寫方法名稱父類已經宣告好了,所以我們在此處只需要關係子類的構造方法即可。比如,如果要進行檔案的操作,可以使用FileOutputStream類來處理,這個類的構造方法如下:
a.接收File類(覆蓋):public FileOutputStream(File file) throws FileNotFoundException
b.接收File類(追加):public FileOutputStream(FIle file,boolean appeand)
下面是例子:

//1.位元組輸出流(檔案追加)
File file =new File("E:"+File.separator+"learn"+File.separator+"javaio"+File.separator+"a1");
if(!file.getParentFile().exists()){
    file.getParentFile().mkdirs();//建立多級目錄
}
//OutputStream是一個抽象類,所以要想例項化物件必須通過其子類來例項化物件
        OutputStream output=new FileOutputStream(file,true);
//將第二個引數寫為true,這表示我們這個檔案可以進行追加,就是當我們每次寫的時候都是從
//上一次的內容開始寫的。如果不寫第二個引數,那麼預設的是false。表示這個檔案不會進行追加
        String msg="你好\n";
        //要通過OutputStream例項化的物件呼叫write()方法,將內容寫進檔案中,需要傳入的引數必須是一個位元組陣列
        // 所以我們需要將內容變為位元組陣列,作為引數傳遞,將內容寫進檔案中。
        output.write(msg.getBytes());

在這裡插入圖片描述
上面的操作我們可以將內容通過程式寫進檔案中,經過兩次執行以後我們的結果是兩個你好,表示這是在上一次的內容上進行追加的。

//部分內容輸出
File file = new File("E:"+File.separator+"learn"+File.separator+"javaio"+File.separator+"a1") ;
if(!file.getParentFile().exists()){//必須保證父目錄存在
    file.getParentFile().mkdirs();
}
OutputStream output=new FileOutputStream(file,true);
//下面是輸出到檔案的內容
        String msg="如果巴黎不快樂";
        //將內容變為字元陣列
        output.write(msg.getBytes());
        output.close();
    }
}

1.2AutoCloseable自動關閉支援
對於上面的操作我們最後讀需要做同一個動作就是將流關閉,但是在JDK1.7以後我們有了一個AutoCloseable自動關閉支援,雖然是出現了這種處理方法,但是它需要結合我們的try.catch使用
例子:

//2.使用自動關閉處理之前的操作
public class Practice{
    public static void main(String[] args) {
        File file =new File("E:"+File.separator+"learn"+File.separator+"javaio"+File.separator+"a1");
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        //和之前輸出到檔案內容的操作方法不一樣了。我們將定義物件這個操作方法放在了異常處理定義物件裡面,
        //OutputStream是實現了AutoCloseable的介面的。
        try(OutputStream output = new FileOutputStream(file,true)){
            String msg="你和我的傾城時光";
            output.write(msg.getBytes());
            //由於我們使用了這個異常處理,所以不需要在後面寫一個關閉流語句。
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

雖然我們有了自動關閉這個方法,但是這個使用的時候會引起結構混亂,所以我們還是使用手動關閉更好,不容易出錯。
2.位元組輸入流:InputStream
前面我們講了OutputStream輸出流對檔案內容的處理,下面我們學習InputStream類在程式中讀取檔案內容。
InputStream類的定義如下:
public abstract class InputStream implements Closeable
我們可以看到,我們的InputStream方法只實現了Closeable介面。下面是InputStream類中的方法:
(1)讀取資料到位元組陣列中,返回資料的讀取個數。它將會遇到下面幾種情況:
如果此時開闢的位元組陣列大小大於讀取的資料大小,則返回的就是讀取個數;
如果讀取的資料大於陣列最大能放下的長度,那麼返回的就是陣列的長度;
如果沒有資料了,還是進行讀,那麼返回的是-1;
public int read(byte b[]) throws IOExcepion.
(2)讀取部分資料到位元組陣列中,每次只讀取傳遞陣列的內容:
那麼這個時候如果讀取滿了就返回長度;
如果沒有讀取滿,這返回讀取的資料個數;
如果讀取到最後沒有資料了則返回-1;
public int reand(byte b[],int off,int len) throws IOException
(3)讀取單個位元組,每次讀取一個位元組的內容,直到沒有資料了返回-1:
public abstract int read() throws IOException

2.1.實現檔案資訊的讀取

public class TestInputStream {
    public static void main(String[] args) throws IOException {
        //1.定義檔案路徑-->採用Path的方法
        Path path=Paths.get("E:","learn","javaio","a1");
        File file=path.toFile();//將路徑轉化為檔案
        //注意:當我們使用Path方法例項化File物件的時候就不需要進行判斷檔案是否存在了,因為
        //toFile()就是將路徑轉化為了檔案,所以我們的檔案是已經存在了的。
            InputStream input = new FileInputStream(file);
            byte[] data=new byte[1024];//每次可以讀取的最大數量
            int len =input.read(data);//此時已經將檔案中的資料按照位元組讀取到了陣列中
            String result=new String(data,0,len);//將位元組陣列轉化為String型別
        System.out.println("讀取到的內容為:"+result);
        input.close();
    }
}

在這裡插入圖片描述
三、字元流
1.字元輸出流
字元主要適合處理中文資料,Writer是字元輸出流的處理類,這個類的定義如下:
public abstract class Writer implements Appendable,Flushable
當然在我們的Writer類裡面也提供了writer方法,而且該方法接收的型別都是char型別,此外Writer還提供了一個直接輸出字串的方法:
public void writer(String str) throws IOException
注意:要操作檔案使用FileWriter子類。
下面是通過Writer實現輸出流(將內容寫進檔案):

public class TestWriter {
    //字元輸出流(注意我們的磁碟資料儲存的單位都是位元組,所以我們的字元需要處理)
    public static void main(String[] args) throws IOException {
        Path path= Paths.get("E:","learn","javaio","a1");
        File file=path.toFile();
        String msg="裂縫中的陽光";
        //Writer是字元輸出流的類,它也是抽象類,例項化它的物件需要通過它的子類FileWriter
        Writer out=new FileWriter(file);//在字元輸出流中沒有追加的這個方法
        //由於在字元輸出流類的方法中的這個write()方法的引數可以是字串型的,所以我們可以直接將字串傳入,
        // 然後將內容寫到檔案中。
        out.write(msg);
        out.close();
    }
}

在使用字元輸出流之前檔案中的內容:
在這裡插入圖片描述
使用字元輸出流以後檔案中的內容:
在這裡插入圖片描述
從以上結果我們可以得到結論,雖然字元輸出流方法與我們的OutputStream名方法非常相似,它直接提供了直接寫入String內容的方法,不需要將字串轉化為位元組陣列再寫入檔案中。但是字元輸出流沒有追加功能,每次寫入一次,檔案中的內容就被更新以後再寫入。
2.字元輸出流:Reader
Reader也是一個抽象類。如果需要進行讀取任然需要FileReader,在Writer中我們有直接寫入的方法,在Reader中我們同樣也有直接讀取的方法。
eg:
通過檔案讀取資料:

//字元輸入流(讀取檔案中的資料)
public class TestReader {
    public static void main(String[] args) throws IOException {
        File file=new File("E:"+ File.separator+"learn"+File.separator+"javaio"+File.separator+"a1");
        if(file.exists()){
            Reader in=new FileReader(file);
            char[] data=new char[1024];//最大可以讀取的範圍
            int len=in.read(data);//通過read返回的是讀取到的長度
            //將陣列轉化為字串
            String result=new String(data,0,len);
            System.out.println("讀取到的內容為:"+result);
            in.close();
        }
    }
}

在這裡插入圖片描述
3.位元組流與字元流的對比
通過上面對位元組流和字元流的學習我們可以看到位元組流和字元流在編寫程式碼上大體是相同的,但是我們知道位元組流適用於所有的資料型別,但是我們的字元流只適用於中文,所以我們有限選擇的是位元組流,只有在處理中文的時候我們才選取字元流;因為所有的字元流都需要通過記憶體緩衝來處理的,因為字元是經過了中間的處理的,從終端到輸出(從輸入到終端)的時候,緩衝區就是中間將位元組處理為字元的地方,這些資料都先儲存在緩衝區的。如果我們的字元流沒有關閉,那麼資料就可能儲存在快取中並沒有輸出到目標源。這個時候需要我們強制手動的重新整理緩衝區才能得到完整的資料,所以它是有弊端的。
eg:
下面我們就看看重新整理操作:

//字元流重新整理操作
public class TestWriter {
    //字元輸出流(注意我們的磁碟資料儲存的單位都是位元組,所以我們的字元需要處理)
    public static void main(String[] args) throws IOException {
        Path path= Paths.get("E:","learn","javaio","a1");
        File file=path.toFile();
        String msg="裂縫中的陽光2222";
        //Writer是字元輸出流的類,它也是抽象類,例項化它的物件需要通過它的子類FileWriter
        Writer out=new FileWriter(file);//在字元輸出流中沒有追加的這個方法
        //由於在字元輸出流類的方法中的這個write()方法的引數可以是字串型的,所以我們可以直接將字串傳入,
        // 然後將內容寫到檔案中。
        out.write(msg);
        out.flush();
    }
}

沒有關閉流並且沒有重新整理的情況:
在這裡插入圖片描述
沒有關閉流但是手動重新整理了:
在這裡插入圖片描述
所以我們總結一下:
以後再進行I/O處理的時候,如果處理的是圖片、音樂、文字都可以使用位元組流,但是隻有在處理中文的時候才可以使用。