Java學習總結:42(字節流和字符流)

字節流與字符流

上一節我們學習了文件操作類File,但是File類雖然可以操作文件,但是卻不能操作文件的內容。如果要進行文件內容的操作,就必須依靠流的概念來完成。流在實際中分爲輸入流和輸出流兩種,輸入流和輸出流是一種相對的概念,關鍵是要看參考點。

Java中針對數據流的操作也分爲輸入與輸出兩種方式,並且提供了以下的支持:
字節流:InputStream(輸入字節流)、OutputStream(輸出字節流);
字符流:Reader(輸入字符流)、Writer(輸出字符流)

注意:這四個操作流的類都屬於抽象類,所以在使用這些類時,必須通過子類對象向上轉型來進行抽象類的實例化操作。

流的基本操作形式:

  1. 通過File類定義一個要操作文件的路徑;
  2. 通過字節流的子類對象爲父類對象實例化;
  3. 進行數據的讀(輸入)、寫(輸出)操作;
  4. 數據流屬於資源操作,資源操作必須關閉。
    其中最重要的式第四步,不管何種情況,只要是資源操作(例如:網絡、文件、數據庫的操作都屬於資源操作),就必須關閉連接(幾乎每種類都會提供close()方法)。

字節輸出流:OutputStream

OutputStream類的常用方法

No. 方法 類型 描述
1 public void close() throws IOException 普通 關閉字節輸出流
2 public void flush() throws IOException 普通 強制刷新
3 public abstract void write(int b) throws IOException 普通 輸出單個字節
4 public void write(byte[] b) throws IOException 普通 輸出全部字節數組數據
5 public void write(byte[] b,int off,int len) throws IOException 普通 輸出部分字節數組數據

關於OutputStream類的組成說明
OutputStream是一個抽象類,該類的定義如下:

public abstract class OutputStream extends Object implement Closeable,Flushable

由上我們可以發現OutputStream類同時實現了Closeable與Flushable兩個父接口,而這兩個父接口的定義如下:

Closeable接口
public interface Closeable extends AutoCloseable{
	public void close() throws IOException;
}
Flushable接口
public interface Flushable{
	public void flush() throws IOException
}

通過定義我們可以發現Closeable接口聲明繼承了AutoCloseable(自動關閉)父接口,該接口定義如下:

public interface AutoCloseable{
	public void close() throws Excption;
}

通過AutoCloseable接口系統會自動幫助我們(即用戶)調用close()方法釋放資源。

例:自動執行close()操作

package Project.Study.OutputStreamClass;

class Net implements AutoCloseable{
    @Override
    public void close()throws Exception {
        System.out.println("資源自動關閉,釋放資源");
    }
    public void info()throws Exception {//假設有異常拋出
        System.out.println("...");
    }
}
public class Test1 {
    public static void main(String[]args){
        try(Net net=new Net()) {
            net.info();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
//結果:
//...
//資源自動關閉,釋放資源

OutputStream類本身是一個抽象類,這樣就需要一個子類。所以,可以使用FileOutputStream子類完成操作。

FileOutputStream類的常用方法

No. 方法 類型 描述
1 public FileOutputStream(File file)throws FileNotFoundException 構造 將內容輸出到指定路徑,如果文件已經存在,則使用新的內容覆蓋舊的內容
2 public FileOutputStream(File file,boolean append)throws FileNotFoundException 構造 如果將布爾參數設置爲true,表示追加新的內容到文件中

例:文件內容的輸出

package Project.Study.OutputStreamClass;

import java.io.*;

public class Test2 {
    public static void main(String[]args)throws Exception{
        //1.定義要輸出文件的路徑
        File file=new File("d:"+File.separator+"Test"+File.separator+"Test.txt");
        if (!file.getParentFile().exists()){//文件目錄不存在
            file.getParentFile().mkdirs();  //創建目錄
        }
        //2.應使用OutputStream和其子類進行對象的實例化,此時目錄存在,文件還不存在
        OutputStream outputStream=new FileOutputStream(file);
        //字節流輸出需要使用byte類型,需要將String類對象變爲字節數組
        String str="Hello World!!!";
        byte[]data=str.getBytes();  //將字符串變爲字節數組
        outputStream.write(data);   //輸出內容
        outputStream.close();       //資源操作的最後一定要進行關閉
    }
}

結果:
在這裏插入圖片描述

例:採用單個字節的方式輸出

package Project.Study.OutputStreamClass;
import java.io.*;
public class Test3 {
    public static void main(String[]args)throws Exception{
        //1.定義要輸出文件的路徑
        File file=new File("d:"+File.separator+"Test"+File.separator+"Test.txt");
        if (!file.getParentFile().exists()){//文件目錄不存在
            file.getParentFile().mkdirs();  //創建目錄
        }
        //2.應使用OutputStream和其子類進行對象的實例化,此時目錄存在,文件還不存在
        OutputStream outputStream=new FileOutputStream(file);
        //字節流輸出需要使用byte類型,需要將String類對象變爲字節數組
        String str="Hello World!!!";
        byte[]data=str.getBytes();  //將字符串變爲字節數組
        for (int x=0;x<data.length;x++){
            outputStream.write(data[x]);//內容輸出
        }
        outputStream.close();       //資源操作的最後一定要進行關閉
    }
}

例:輸出部分字節數組內容(設置數組的開始索引和長度)

package Project.Study.OutputStreamClass;

import java.io.*;

public class Test4 {
    public static void main(String[]args)throws Exception{
        //1.定義要輸出文件的路徑
        File file=new File("d:"+File.separator+"Test"+File.separator+"Test.txt");
        if (!file.getParentFile().exists()){//文件目錄不存在
            file.getParentFile().mkdirs();  //創建目錄
        }
        //2.應使用OutputStream和其子類進行對象的實例化,此時目錄存在,文件還不存在
        OutputStream outputStream=new FileOutputStream(file);
        //字節流輸出需要使用byte類型,需要將String類對象變爲字節數組
        String str="Hello World!!!";
        byte[]data=str.getBytes();  //將字符串變爲字節數組
        outputStream.write(data,0,6);//內容輸出
        outputStream.close();       //資源操作的最後一定要進行關閉
    }
}

結果:
在這裏插入圖片描述
注意:採用上面這種方法輸出時,要注意數組越界的問題

我們上面三個文件操作都是對文件內容的覆蓋,而如果要實現文件的追加操作可以使用public FileOutputStream(File file,boolean append)的構造方法。

例:文件追加

package Project.Study.OutputStreamClass;

import java.io.*;

public class Test3 {
    public static void main(String[]args) throws Exception {
        //1.定義要輸出文件的路徑
        File file=new File("d:"+File.separator+"Test"+File.separator+"Test.txt");
        if (!file.getParentFile().exists()){//文件目錄不存在
            file.getParentFile().mkdirs();  //創建目錄
        }
        //2.應使用OutputStream和其子類進行對象的實例化,此時目錄存在,文件還不存在
        OutputStream outputStream=new FileOutputStream(file,true);//追加模式
        //字節流輸出需要使用byte類型,需要將String類對象變爲字節數組
        String str="Hello World!!!";
        byte[]data=str.getBytes();  //將字符串變爲字節數組
        outputStream.write(data,5,9);//內容輸出
        outputStream.close();       //資源操作的最後一定要進行關閉
    }
}

結果:
在這裏插入圖片描述

字節輸入流:InputStream

如果要進行文件數據的讀取操作,就可以用java.io.InputStream類完成,此類可以完成字節數據的讀取操作。

InputStream類的常用方法

No. 方法 類型 描述
1 public void close() throws IOException 普通 關閉字節輸入流
2 public abstract int read() throws IOException 普通 讀取單個字節
3 public int read(byte[] b) throws IOException 普通 將數據讀取到字節數組中,同時返回讀取長度
4 public int read(byte[] b,int off,int len) throws IOException 普通 將數據讀取到部分字節數組中,同時返回讀取的數據長度

InputStream類依舊屬於一個抽象類,此類的定義如下:

public abstract class InputStream extends Object implements Closeable

通過定義可以發現InputStream類也實現了Closeable接口(繼承了AutoCloseable接口),所以利用自動關閉的異常處理結構可以實現自動的資源釋放。

關於InputStream類的三個方法的詳細作用:

  • 讀取單個字節:public abstract int read() throws IOException;
    返回值:返回讀取的字節內容,如果已經沒有內容,則讀取後返回“-1”;

  • 將讀取的數據保存在字節數組裏(一次讀取多個數據):public int read(byte[] b) throws IOException;
    返回值:返回讀取的數據長度,如果已經讀取到結尾,則讀取後返回“-1”;

  • 將讀取的數據保存在部分字節數組裏:public int read(byte[] b,int off,int len) throws IOException;
    返回值:讀取的部分數據的長度,如果已經讀取到結尾,則讀取後返回“-1”

java.io.InputStream是一個抽象類,所以如果要進行文件的讀取,需要使用FileInputStream子類,而這個子類的構造方法如下:

方法 類型 描述
public FileInputStream (File file) throws FileNotFoundException 普通 設置要讀取文件數據的路徑

例:數據讀取操作

package Project.Study.InputStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class Test1 {
    public static void main(String[]args) throws Exception {
        File file=new File("d:"+File.separator+"Test"+File.separator+"test.txt");//定義要輸出文件的路徑
        if (file.exists()){                         //判斷文件是否存在後纔可以進行讀取
            InputStream inputStream=new FileInputStream(file);//使用InputStream進行讀取
            byte[]data=new byte[1024];              //準備一個1024的數組
            int len=inputStream.read(data);         //進行數據讀取,將內容保存到字節數組中
            inputStream.close();                    //關閉輸入流
            System.out.println("【"+new String(data,0,len)+"】");//將讀取出來的字節數組變爲字符串進行輸出
        }
    }
}
//結果:
//【Hello World!!!】

例:採用while循環實現輸入流操作

package Project.Study.InputStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class Test2 {
    public static void main(String[]args)throws Exception{
        File file=new File("d:"+File.separator+"Test"+File.separator+"test.txt");//定義輸出文件的路徑
        if (file.exists()){                                         //判斷文件是否存在後纔可以進行讀取
            InputStream inputStream=new FileInputStream(file);      //使用InputStream進行讀取
            byte[]data=new byte[1024];                              //準備一個1024的數組
            int foot=0;                                             //表示字節數組的操作腳標
            int temp=0;                                             //表示接收每次讀取的字節數據
            while((temp=inputStream.read())!=-1){                   //當inputStream.read()!=-1,即輸出文件中還有內容
                data[foot++]=(byte)temp;                            //有內容就進行保存
            }
            inputStream.close();                                    //關閉輸出流
            System.out.println("【"+new String(data,0,foot)+"】");
        }
    }
}
//結果:
//【Hello World!!!】

字符輸出流:Writer

利用Writer類可以直接實現字符數組(包含字符串)的輸出。

Writer類的常用方法

No. 方法 類型 描述
1 public void close() throws IOException 普通 關閉字節輸出流
2 public void flush() throws IOException 普通 強制刷新
3 public Writer append(CharSequence csq) throws IOException 普通 追加數據
4 public void write(String str) throws IOException 普通 輸出字符串數據
5 public void write(char[] cbuf) throws IOException 普通 輸出字符數組數據

通過Writer類定義的方法可以發現,Writer類中直接提供了輸出字符串數據的方法。

Writer類的定義:
Writer類也是屬於抽象類,定義如下:

public abstract class Writer extends Object implement Appendable,Closeable,Flushable

通過繼承結構我們可以發現,Writer類中除了實現Closeable與Flushable接口外,還實現了一個Appendable接口,該接口定義如下:

public interface Appendable{
	public Appendable append(char c)throws IOException;
	public Appendable append(CharSequence csq)throws IOException;
	public Appendable append(CharSequence csq,int start,int end)throws IOException;
}

在Appendable接口中定義了一系列數據追加操作,而追加的類型可以是CharSequence(可以保存String、StringBuffer、StringBuilder類對象)。

因爲Writer是一個抽象類,所以要使用java.io.FileWriter類實現Writer類對象的實例化操作。

FileWriter類的常用方法

No. 方法 類型 描述
1 public FileWriter(File file) throws IOException 構造 設置輸出文件
2 public FileWriter(File file,boolean append) throws IOException 普通 設置輸出文件以及是否進行數據追加

例:使用Writer類實現內容輸出

package Project.Study.WriterClass;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;

public class Test1 {
    public static void main(String[]args)throws Exception{
        File file=new File("d:"+File.separator+"Test1"+File.separator+"test.txt");//定義要輸出文件的路徑
        if (!file.getParentFile().exists()){//判斷目錄是否存在
            file.getParentFile().mkdirs();  //創建文件目錄
        }
        Writer writer=new FileWriter(file); //實例化了Writer類的對象
        String str="Hello World!!!";        //定義輸出內容
        writer.write(str);                  //輸出字符串內容
        writer.close();                     //關閉輸出流
    }
}

結果:
在這裏插入圖片描述

字符輸入流:Reader

java.io.Reader類是實現字符數據輸入的操作類,在進行數據讀取時可以不使用字節數據,而直接依靠字符數據(方便處理中文)進行操作。

Reader類的常用方法

No. 方法 類型 描述
1 public void close() throws IOException 普通 關閉字節輸入流
2 public int read() throws IOException 普通 讀取單個數據
3 public int read() throws IOException 普通 讀取單個字符
4 public int read(char[] cbuf) throws IOException 普通 讀取數據到字符數組中,返回讀取長度
5 public long skip(long n) throws IOException 普通 跳過字節長度

Reader類的定義結構:

public abstract class Reader extends Object implement Readable,Closeable

Readable接口定義如下:

public interface Readable{
	public int read(CharBuffer cb)throws IOException;
}

在Reader接口中定義的read()方法可以將數據保存在CharBuffer(字符緩衝,類似於StringBuffer)對象中,也就是說利用此類對象就可以替代字符數組的操作。

同樣的,因爲Reader類是一個抽象類,要實現文件數據的字符流讀取,可以利用FileReader子類爲Reader類對象實例化。

FileReader類的常用方法如下:

No. 方法 類型 描述
1 public FileReader(File file) throws FileNotFoundException 構造 定義要讀取的文件路徑

例:使用Reader讀取數據

package Project.Study.ReaderClass;

import java.io.File;
import java.io.FileReader;
import java.io.Reader;

public class Test1 {
    public static void main(String[]args)throws Exception{
        File file=new File("d:"+File.separator+"Test1"+File.separator+"test.txt");//定義要輸出的路徑
        if (file.exists()){                                     //判斷文件是否存在
            Reader reader=new FileReader(file);                 //爲Reader對象實例化
            char[]data=new char[1024];                          //開闢字符數組,接收讀取數據
            int len=reader.read(data);                          //進行數據讀取
            reader.close();                                     //關閉輸入流
            System.out.println(new String(data,0,len));
        }
    }
}
//結果:
//Hello World!!!

字節流與字符流的區別

我們以文件操作爲例,字節流與字符流最大的區別就是:字節流直接與終端文件進行數據交互,字符流需要將數據經過緩衝區處理才與終端文件數據交互。在開發中,對字節數據處理是比較多的,而字符流最大的好處是它可以進行中文的有效處理,因此,在開發中,如果要處理中文時應優先考慮字符流,如果沒有中文問題,建議使用字節流。
在使用OutputStream輸出數據時,即使最後沒有關閉輸出流,內容也可以正常輸出,但是反過來如果使用的是字符輸出流Writer,在執行到最後如果不關閉輸出流,就表示在緩衝區中處理的內容不會被強制性清空,所以就不會輸出數據。如果有特殊情況不能關閉字符輸出流,可以使用flush()方法強制清空緩衝區。
例:錯誤示範(不關閉流)

package Project.Study.WriterClass;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;

public class Test2 {
    public static void main(String[]args)throws Exception{
        File file=new File("d:"+File.separator+"Test1"+File.separator+"test2.txt");//定義輸出文件的路徑
        if (!file.getParentFile().exists()){        //判斷文件目錄是否存在
            file.getParentFile().mkdirs();          //若不存在就創建文件目錄
        }
        Writer writer=new FileWriter(file);         //實例化了Writer類的對象
        String str="Hi!!!";                         //定義輸出內容
        writer.write(str);                          //輸出字符串數據
    }
}

結果:
在這裏插入圖片描述
通過上程序執行的結果我們可以看到,此時並沒有輸出結果,輸出文件中什麼也沒有。

例:強制清空字符流緩衝區

package Project.Study.WriterClass;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;

public class Test2 {
    public static void main(String[]args)throws Exception{
        File file=new File("d:"+File.separator+"Test1"+File.separator+"test2.txt");//定義輸出文件的路徑
        if (!file.getParentFile().exists()){        //判斷文件目錄是否存在
            file.getParentFile().mkdirs();          //若不存在就創建文件目錄
        }
        Writer writer=new FileWriter(file);         //實例化了Writer類的對象
        String str="Hi!!!";                         //定義輸出內容
        writer.write(str);                          //輸出字符串數據
        writer.flush();                             //強制刷新緩衝區
    }
}

結果:
在這裏插入圖片描述
上程序執行到最後並沒有執行流的關閉操作,所以從本質上講,內容將無法完整輸出。但因爲利用了flush()方法強制刷新緩衝區,所以它的內容完整輸出了,也就是說,在不關閉流又要完整輸出內容時就只能利用flush()方法強制刷新緩衝區。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章