(十七)Java IO流

本文目錄

1、Java IO流概述

1.1 IO流概述:

1.2 什麼是Java IO流

1.3 IO文件

1.4 字符流和字節流

1.5 IO管道

1.6 Java IO:網絡

1.7 字節和字符數組

1.8 標準輸入輸出流(System.in, System.out, System.err)

1.9 字符流的Buffered和Filter

2 Java IO流相關題目

(1)什麼是IO流?

(2)字節流和字符流的區別。

(3)Java中流類的超類主要由那些?

(4)FileInputStream和FileOutputStream是什麼?

(5)System.out.println()是什麼?

(6)什麼是Filter流?

(7)有哪些可用的Filter流?

(8)在文件拷貝的時候,哪一種流可用提升更多的性能?

(9)說說管道流(Piped Stream)

(10)說說File類

(11)說說RandomAccessFile?


1、Java IO流概述

1.1 IO流概述:

IO流用來處理設備之間的數據傳輸。Java對數據的操作是通過流的方式,操作流的對象都在IO包中。按照數據流的方向不同可以分爲輸入流和輸出流;按照處理數據單位不同可以分爲字節流和字符流;按照實現功能不同可以分爲節點流和處理流。

(1)輸入輸出

Java的IO包主要關注的是從原始數據源的讀取以及輸出原始數據到目標媒介。 以下是最典型的數據源和目標媒介:

  • 文件
  • 管道
  • 網絡連接
  • 內存緩存
  • 標準輸入輸出流(System.in,System.out,System.error)

下圖說明了一個程序從數據源讀取數據,然後將數據輸出到其他媒介的原理:

(2)流

在Java IO中,流是一個核心的概念。流從概念上來說是一個連續的數據流。你既可以從流中讀取數據,也可以往流中寫數據。流與數據源或者數據流向的媒介相關聯。在Java IO中流既可以是字節流(以字節爲單位進行讀寫),也可以是字符流(以字符爲單位進行讀寫)。Java IO包中操作流的類主要有類InputStream, OutputStream, Reader 和Writer 。

一個程序需要InputStream或者Reader從數據源讀取數據,需要OutputStream或者Writer將數據寫入到目標媒介中,如下圖所示:InputStream和Reader與數據源相關聯,OutputStream和Writer與目標媒介相關聯。

(3)Java IO的用途和特徵

Java IO中包含了許多InputStream、OutputStream、Reader、Writer的子類,這樣的設計可以讓每一個類都負責不同的功能。各類的用途彙總如下:

1)文件訪問

2)網絡訪問

3)內存緩存訪問

4)線程內部通信(管道)

5)緩衝

6)過濾

7)解析

8)讀寫文本 (Readers / Writers)

9)讀寫基本類型數據 (long, int etc.)

10)讀寫對象

(4)Java IO類概述

以下是一張通過輸入、輸出、基於字節或者字符、以及其他比如緩衝、解析之類的特定用途劃分的大部分Java IO類的表格:

Java IO類圖如下:

1.2 什麼是Java IO流

Java IO流是既可以從中讀取,也可以寫入到其中的數據流。 流通常會與數據源、數據流向目的地相關聯,比如文件、網絡等等。流和數組不一樣,不能通過索引讀寫數據。在流中,你也不能像數組那樣前後移動讀取數據,除非使用RandomAccessFile 處理文件。流僅僅只是一個連續的數據流。

某些類似PushbackInputStream 流的實現允許你將數據重新推回到流中,以便重新讀取。然而你只能把有限的數據推回流中,並且你不能像操作數組那樣隨意讀取數據。流中的數據只能夠順序訪問。

Java IO流通常是基於字節或者基於字符的。字節流通常以“stream”命名,比如InputStream和OutputStream。除了DataInputStream 和DataOutputStream還能夠讀寫int, long, float和double類型的值以外,其他流在一個操作時間內只能讀取或者寫入一個原始字節。

字符流通常以“Reader”或者“Writer”命名。字符流能夠讀寫字符(比如Latin1或者Unicode字符)。可以瀏覽Java Readers and Writers獲取更多關於字符流輸入輸出的信息。

(1)InputStream

java.io.InputStream類是所有Java IO輸入流的基類。如果你正在開發一個從流中讀取數據的組件,請嘗試用InputStream替代任何它的子類(比如FileInputStream)進行開發。這麼做能夠讓你的代碼兼容任何類型而非某種確定類型的輸入流。

(2)組合流

組合流可以將流整合起來以便實現更高級的輸入和輸出操作。比如,一次讀取一個字節是很慢的,所以可以從磁盤中一次讀取一大塊數據,然後從讀到的數據塊中獲取字節。爲了實現緩衝,可以把InputStream包裝到BufferedInputStream中。 示例如下:

InputStream input = new BufferedInputStream(new FileInputStream("c:\data\input-file.txt"));

緩衝同樣可以應用到OutputStream中。你可以實現將大塊數據批量地寫入到磁盤(或者相應的流)中,這個功能由BufferedOutputStream實現。

緩衝只是通過流整合實現的其中一個效果。你可以把InputStream包裝到PushbackInputStream中,之後可以將讀取過的數據推回到流中重新讀取,在解析過程中有時候這樣做很方便。或者,你可以將兩個InputStream整合成一個SequenceInputStream。

將不同的流整合到一個鏈中,可以實現更多種高級操作。通過編寫包裝了標準流的類,可以實現你想要的效果和過濾器。

1.3 IO文件

在Java應用程序中,文件是一種常用的數據源或者存儲數據的媒介。

(1)通過Java IO讀文件

如果你需要在不同端之間讀取文件,你可以根據該文件是二進制文件還是文本文件來選擇使用FileInputStream或者FileReader。

這兩個類允許你從文件開始到文件末尾一次讀取一個字節或者字符,或者將讀取到的字節寫入到字節數組或者字符數組。你不必一次性讀取整個文件,相反你可以按順序地讀取文件中的字節和字符。

如果你需要跳躍式地讀取文件其中的某些部分,可以使用RandomAccessFile。

(2)通過Java IO寫文件

如果你需要在不同端之間進行文件的寫入,你可以根據你要寫入的數據是二進制型數據還是字符型數據選用FileOutputStream或者FileWriter。

可以一次寫入一個字節或者字符到文件中,也可以直接寫入一個字節數組或者字符數據。數據按照寫入的順序存儲在文件當中。

(3)通過Java IO隨機存取文件

隨機存取並不意味着你可以在真正隨機的位置進行讀寫操作,它只是意味着你可以跳過文件中某些部分進行操作,並且支持同時讀寫,不要求特定的存取順序。

這使得RandomAccessFile可以覆蓋一個文件的某些部分、或者追加內容到它的末尾、或者刪除它的某些內容,當然它也可以從文件的任何位置開始讀取文件。示例如下:

@Test
    //文件流範例,打開一個文件的輸入流,讀取到字節數組,再寫入另一個文件的輸出流
    public void test1() {
        try {
            FileInputStream fileInputStream = new FileInputStream(new File("a.txt"));
            FileOutputStream fileOutputStream = new FileOutputStream(new File("b.txt"));
            byte []buffer = new byte[128];
            while (fileInputStream.read(buffer) != -1) {
                fileOutputStream.write(buffer);
            }
            //隨機讀寫,通過mode參數來決定讀或者寫
            RandomAccessFile randomAccessFile = new RandomAccessFile(new File("c.txt"), "rw");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.4 字符流和字節流

Java IO的Reader和Writer除了基於字符之外,其他方面都與InputStream和OutputStream非常類似。他們被用於讀寫文本。

(1)Reader

Reader類是Java IO中所有Reader的基類。子類包括BufferedReader,PushbackReader,InputStreamReader,StringReader和其他Reader。

(2)Writer

Writer類是Java IO中所有Writer的基類。子類包括BufferedWriter和PrintWriter等等。

代碼示例:

Reader reader = new FileReader("c:\\data\\myfile.txt");

int data = reader.read();

while(data != -1){

    char dataChar = (char) data;

    data = reader.read();

}

(3)整合Reader與InputStream

一個Reader可以和一個InputStream相結合。如果你有一個InputStream輸入流,並且想從其中讀取字符,可以把這個InputStream包裝到InputStreamReader中。把InputStream傳遞到InputStreamReader的構造函數中:

Reader reader = new InputStreamReader(inputStream);

(4)與Reader和InputStream類似,一個Writer可以和一個OutputStream相結合。把OutputStream包裝到OutputStreamWriter中,所有寫入到OutputStreamWriter的字符都將會傳遞給OutputStream。這是一個OutputStreamWriter的例子:

Writer writer = new OutputStreamWriter(outputStream);

1.5 IO管道

Java IO中的管道爲運行在同一個JVM中的兩個線程提供了通信的能力。所以管道也可以作爲數據源以及目標媒介。

你不能利用管道與不同的JVM中的線程通信(不同的進程)。在概念上,Java的管道不同於Unix/Linux系統中的管道。在Unix/Linux中,運行在不同地址空間的兩個進程可以通過管道通信。在Java中,通信的雙方應該是運行在同一進程中的不同線程。

(1)通過Java IO創建管道:

可以通過Java IO中的PipedOutputStream和PipedInputStream創建管道。一個PipedInputStream流應該和一個PipedOutputStream流相關聯。

Java IO管道示例:這是一個如何將PipedInputStream和PipedOutputStream關聯起來的簡單例子:

//使用管道來完成兩個線程間的數據點對點傳遞
    @Test
    public void test2() throws IOException {
        PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    pipedOutputStream.write("hello input".getBytes());
                    pipedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    byte []arr = new byte[128];
                    while (pipedInputStream.read(arr) != -1) {
                        System.out.println(Arrays.toString(arr));
                    }
                    pipedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

(2)管道和線程

需要注意,當使用兩個相關聯的管道流時,務必將它們分配給不同的線程。read()方法和write()方法調用時會導致流阻塞,這意味着如果你嘗試在一個線程中同時進行讀和寫,可能會導致線程死鎖。

(3)管道的替代

除了管道之外,一個JVM中不同線程之間還有許多通信的方式。實際上,線程在大多數情況下會傳遞完整的對象信息而非原始的字節數據。但是,如果你需要在線程之間傳遞字節數據,Java IO的管道是一個不錯的選擇。

1.6 Java IO:網絡

Java中網絡的內容或多或少的超出了Java IO的範疇。關於Java網絡更多的是在我的Java網絡教程中探討。但是既然網絡是一個常見的數據來源以及數據流目的地,並且因爲你使用Java IO的API通過網絡連接進行通信,所以本文將簡要的涉及網絡應用。

當兩個進程之間建立了網絡連接之後,他們通信的方式如同操作文件一樣:利用InputStream讀取數據,利用OutputStream寫入數據。換句話來說,Java網絡API用來在不同進程之間建立網絡連接,而Java IO則用來在建立了連接之後的進程之間交換數據。

基本上意味着如果你有一份能夠對文件進行寫入某些數據的代碼,那麼這些數據也可以很容易地寫入到網絡連接中去。你所需要做的僅僅只是在代碼中利用OutputStream替代FileOutputStream進行數據的寫入。因爲FileOutputStream是OuputStream的子類,所以這麼做並沒有什麼問題。示例:

//從網絡中讀取字節流也可以直接使用OutputStream
public void test3() {
    //讀取網絡進程的輸出流
    OutputStream outputStream = new OutputStream() {
        @Override
        public void write(int b) throws IOException {
        }
    };
}
public void process(OutputStream ouput) throws IOException {
    //處理網絡信息
    //do something with the OutputStream
}

1.7 字節和字符數組

從InputStream或者Reader中讀入數組,從OutputStream或者Writer中寫數組。在java中常用字節和字符數組在應用中臨時存儲數據。而這些數組又是通常的數據讀取來源或者寫入目的地。如果你需要在程序運行時需要大量讀取文件裏的內容,那麼你也可以把一個文件加載到數組中。

//字符數組和字節數組在io過程中的作用
    public void test4() {
        //arr和brr分別作爲數據源
        char []arr = {'a','c','d'};
        CharArrayReader charArrayReader = new CharArrayReader(arr);
        byte []brr = {1,2,3,4,5};
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(brr);
    }

 

1.8 標準輸入輸出流(System.in, System.out, System.err)

System.in, System.out, System.err這3個流同樣是常見的數據來源和數據流目的地。使用最多的可能是在控制檯程序裏利用System.out將輸出打印到控制檯上。

JVM啓動的時候通過Java運行時初始化這3個流,所以你不需要初始化它們(儘管你可以在運行時替換掉它們)。

(1)System.in

System.in是一個典型的連接控制檯程序和鍵盤輸入的InputStream流。通常當數據通過命令行參數或者配置文件傳遞給命令行Java程序的時候,System.in並不是很常用。圖形界面程序通過界面傳遞參數給程序,這是一塊單獨的Java IO輸入機制。

(2)System.out

System.out是一個PrintStream流。System.out一般會把你寫到其中的數據輸出到控制檯上。System.out通常僅用在類似命令行工具的控制檯程序上。System.out也經常用於打印程序的調試信息(儘管它可能並不是獲取程序調試信息的最佳方式)。

(3)System.err

System.err是一個PrintStream流。System.err與System.out的運行方式類似,但它更多的是用於打印錯誤文本。一些類似Eclipse的程序,爲了讓錯誤信息更加顯眼,會將錯誤信息以紅色文本的形式通過System.err輸出到控制檯上。

1.9 字符流的Buffered和Filter

BufferedReader能爲字符輸入流提供緩衝區,可以提高許多IO處理的速度。你可以一次讀取一大塊的數據,而不需要每次從網絡或者磁盤中一次讀取一個字節。特別是在訪問大量磁盤數據時,緩衝通常會讓IO快上許多。

BufferedReader和BufferedInputStream的主要區別在於,BufferedReader操作字符,而BufferedInputStream操作原始字節。只需要把Reader包裝到BufferedReader中,就可以爲Reader添加緩衝區(譯者注:默認緩衝區大小爲8192字節,即8KB)。代碼如下:

Reader input = new BufferedReader(new FileReader("c:\\data\\input-file.txt"));

你也可以通過傳遞構造函數的第二個參數,指定緩衝區大小,代碼如下:

Reader input = new BufferedReader(new FileReader("c:\\data\\input-file.txt"), 8 * 1024);

這個例子設置了8KB的緩衝區。最好把緩衝區大小設置成1024字節的整數倍,這樣能更高效地利用內置緩衝區的磁盤。除了能夠爲輸入流提供緩衝區以外,其餘方面BufferedReader基本與Reader類似。BufferedReader還有一個額外readLine()方法,可以方便地一次性讀取一整行字符。

 

(1)BufferedWriter

與BufferedReader類似,BufferedWriter可以爲輸出流提供緩衝區。可以構造一個使用默認大小緩衝區的BufferedWriter(譯者注:默認緩衝區大小8 * 1024B),代碼如下:

Writer writer = new BufferedWriter(new FileWriter("c:\\data\\output-file.txt"));

也可以手動設置緩衝區大小,代碼如下:

Writer writer = new BufferedWriter(new FileWriter("c:\\data\\output-file.txt"), 8 * 1024);

爲了更好地使用內置緩衝區的磁盤,同樣建議把緩衝區大小設置成1024的整數倍。除了能夠爲輸出流提供緩衝區以外,其餘方面BufferedWriter基本與Writer類似。類似地,BufferedWriter也提供了writeLine()方法,能夠把一行字符寫入到底層的字符輸出流中。

值得注意是,你需要手動flush()方法確保寫入到此輸出流的數據真正寫入到磁盤或者網絡中。

(2)FilterReader

與FilterInputStream類似,FilterReader是實現自定義過濾輸入字符流的基類,基本上它僅僅只是簡單覆蓋了Reader中的所有方法。

2 Java IO流相關題目

(1)什麼是IO流?

它是一種數據的流從源頭流到目的地。比如文件拷貝,輸入流和輸出流都包括了。輸入流從文件中讀取數據存儲到進程(process)中,輸出流從進程中讀取數據然後寫入到目標文件。

(2)字節流和字符流的區別。

字節流在JDK1.0中就被引進了,用於操作包含ASCII字符的文件。JAVA也支持其他的字符如Unicode,爲了讀取包含Unicode字符的文件,JAVA語言設計者在JDK1.1中引入了字符流。ASCII作爲Unicode的子集,對於英語字符的文件,可以可以使用字節流也可以使用字符流。

(3)Java中流類的超類主要由那些?

java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer

 

(4)FileInputStream和FileOutputStream是什麼?

這是在拷貝文件操作的時候,經常用到的兩個類。在處理小文件的時候,它們性能表現還不錯,在大文件的時候,最好使用BufferedInputStream (或 BufferedReader) 和 BufferedOutputStream (或 BufferedWriter)

(5)System.out.println()是什麼?

println是PrintStream的一個方法。out是一個靜態PrintStream類型的成員變量,System是一個java.lang包中的類,用於和底層的操作系統進行交互。

(6)什麼是Filter流?

Filter Stream是一種IO流,主要作用是用來對存在的流增加一些額外的功能,像給目標文件增加源文件中不存在的行數,或者增加拷貝的性能。

(7)有哪些可用的Filter流?

在java.io包中主要由4個可用的filter Stream。兩個字節filter stream兩個字符filter stream.分別是FilterInputStream, FilterOutputStream,FilterReader and FilterWriter.這些類是抽象類,不能被實例化的。

(8)在文件拷貝的時候,哪一種流可用提升更多的性能?

在字節流的時候,使用BufferedInputStream和BufferedOutputStream。 在字符流的時候,使用BufferedReader 和 BufferedWriter

(9)說說管道流(Piped Stream)

有四種管道流, PipedInputStream, PipedOutputStream, PipedReader 和 PipedWriter.在多個線程或進程中傳遞數據的時候管道流非常有用。

(10)說說File類

它不屬於 IO流,也不是用於文件操作的,它主要用於知道一個文件的屬性,讀寫權限,大小等信息。

(11)說說RandomAccessFile?

它在java.io包中是一個特殊的類,既不是輸入流也不是輸出流,它兩者都可以做到。他是Object的直接子類。通常來說,一個流只有一個功能,要麼讀,要麼寫。但是RandomAccessFile既可以讀文件,也可以寫文件。 DataInputStream 和 DataOutStream有的方法,在RandomAccessFile中都存在。

 

附言:

本文整理來源於網絡、博客等資源,僅做個人學習筆記複習所用。

如果對你學習有用,請點贊共同學習!

如有侵權,請聯繫我刪!

 

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