I/O輸入/輸出

引言

對程序語言設計者來說,設計一個令人滿意的I/O(輸入輸出)系統,是件極艱巨的任務,摘自《Thinking in Java 》

概述

JAVA程序通過流來完成輸入/輸出。流是生產或消費信息的抽象,流通過Java的輸入/輸出系統與物理設備連接。JAVA的I/O通過java.io包下的類和接口來支持,主要是5個類和一個接口。

  • File:用于文件或目錄的描述信息,例如生成新目錄,修改文件名,刪除文件,判斷文件路徑等
  • InputStream:抽象類,所有輸入流的父類,定義了所有輸入流具有的共同特征。
  • OutputStream:抽象類,基于字節的輸出操作,所有輸出流的父類,定義了輸出流具有的共同特征。
  • Reader:抽象類,基于字符的輸入類。
  • Writer:抽象類,基于字符的輸出類。
  • Serializable:將Java對象轉換成平臺無關的二進制數據
  • RandomAccessFile:包裝了一個隨機訪問的文件,實現了DataInput/DataOutput接口。
File類

一個File類的對象,表示了磁盤上的文件或目錄,提供了與平臺無關的方法來對磁盤上的文件或者目錄進行操作,直接處理文件或目錄,但是不能訪問文件本身。

在java.io包中提供了60多了類流:

從功能上分為:

  • 輸入流:從外部流向內存
  • 輸出流:從內存流向外部

從流的角色上分為:

  • 節點流:可以從/向一個特定的IO設備(磁盤,網絡)讀/寫數據的流,直接連接物理設備或socket
  • 處理流:對一個已經存在的流(節點流)進行連接或者封裝。

從流的結構上分為:

  • 字節流 :InputStream OutputStream
  • 字符流 : Reader Writer

在最底層,所有的輸入輸出都是以字節為單位的,基于字符的流只是為處理字符提供方便有效的方法。它們的操作方式幾乎完全一樣,只是操作的數據單元不同,字節流操作的數據單元是字符,字符流操作的數據單元是字符。

類流全家福

I/O流類.png

從上面的圖片中,可以看出無論是字節流和字符流都提供了對文件,數組,字符串,管道,對象,音頻等內容的輸入/輸出流支持。

對輸入/輸出流基類的簡單介紹

InputSteam 三個基本的讀方法:
abstract int read(): 讀取一個字節數據,并返回讀到數據的末尾,如果返回-1 , 表示都到了輸入流的末尾。

int read(byte[] b ): 將數據讀入一個字節數組,同時返回實際讀取的字節數,如果返回-1 ,表示讀到了輸入流的末尾。

int read(byte[] b , int off , int len):將數據讀入一個字節數組,同時返回實際讀取的字節數,如果返回-1 ,表示讀到了輸入流的末尾,off指定在數組b中存放數據的起始位置,len指定讀取的最大字節數。

可能會有一個疑問,為什么上面的第一個方法是抽象,而其余兩個read方法是具體的? 通過源碼找到了想要的答案。第二個方法依靠了第三個方法,而第三個方法依靠了第一個方法實現,換句話說只有第一個read方法是與具體的I/O設備相關的,需要InputStream的子類來實現。源碼如下:

public abstract int read() throws IOException;

public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);  //調用第三個方法實現
    }

public int read(byte b[], int off, int len) throws IOException {
        //判斷參數的合法性
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
        
        int c = read();   //調用第一個方法,讀取一個字節
        if (c == -1) {
            return -1;   //沒有字節返回-1 ,讀取結束
        }
        b[off] = (byte)c;  //將讀取到的字節放入數組中

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;  //將讀取到的字節放入數組中,讀取的長度大于數據長度,循環讀取結束
            }
        } catch (IOException ee) {
        }
        return i;  //返回讀取的字節數組長度
    }

其它方法
long skip(long n ):輸入流中跳過n個字節,并返回實際跳過的字節數
void close():關閉流
。。。

OutputStream 主要方法
void write(byte[]] b ):將參數b中的字節寫到輸出流
void write(byte[] b , int off ,int len):將參數b的從偏移量off開始len個字節寫到輸出流
abstract viud write(int b ):先將int轉換成byte類型,把低字節寫入到輸出流中
void flush():將緩沖區中的字節全部輸出,并清空緩存區
void close():關閉輸出流并釋放與流相關的系統資源

Reader里包含的3個讀取方法:
int read():
int read(char[] c):
int read(char[] c ,int off , int len) :

Write包含的方法
void write(char[] c)
void write(String str)
void write(String str, int off, int len)
abstract flush()
void close()

根據上面圖片做一個簡單的總結:

  • FileInputStream和FileOutputStream 節點流,用于對文件讀取或往文件中寫入字節流。如果在構造FileOutputStream時,文件已經存在,則覆蓋整個文件。
  • BufferedInputStream和BufferedOutputStream過濾流,需要使用已經存在的節點流來構造,提供了讀寫的效率。
  • DataInputStream和DataOutputStream 過濾流,需要使用已經存在的節點流來構造,提供了讀寫Java中基本數據類型的功能。
  • PipedInputStream和PipedOutputStream 管道流,用于線程間的通信,一個線程的PipedInputStream對象從另一個線程的PipedOutputStream 對象讀取輸入,只要管道流有用,必須提供同時構造管道輸入流和管道輸出流。
  • InputStreamReader和OutputStreamWriter類,用于處理字符流的基本類,用來在字節流和字符流之間搭一座橋。將字節流和字符流進行轉換。
  • FileReader和FileWriter 可以創建一個可以讀/寫文件的類。
  • BufferedReader:通過緩沖輸入提高性能。
  • SequenceInputStream:把多個InputStream合并為一個InputStream

I/O流的鏈接示意圖:

流的鏈接示意圖.png

常用類
FileInputStream , FileOutputStream , ByteArrayInputStream, ByteArrayOutputStream ,BufferedReader,BufferedWriter, ,InputStreamReader ,InputStreamWriter,FileReader ,FileWriter

如何選擇I/O流

確定是輸入還是輸出

輸入:輸入流 InputStream Reader
輸出:輸出流 OutputStream Writer

明確操作的數據對象是否是純文本

是:字符流 Reader,Writer
否:字節流 InputStream,OutputStream

明確具體的設備。

文件:
讀:FileInputStream,, FileReader,
寫:FileOutputStream,FileWriter

數組:
byte[ ]:ByteArrayInputStream, ByteArrayOutputStream
char[ ]:CharArrayReader, CharArrayWriter

String:
StringBufferInputStream(已過時,因為其只能用于String的每個字符都是8位的字符串), StringReader, StringWriter

Socket流
鍵盤:用System.in(是一個InputStream對象)讀取,用System.out(是一個OutoutStream對象)打印

是否需要轉換流

是,就使用轉換流,從Stream轉化為Reader、Writer:InputStreamReader,OutputStreamWriter

是否需要緩沖提高效率

是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader, BufferedWrite


少年聽雨歌樓上,紅燭昏羅帳。  
壯年聽雨客舟中,江闊云低,斷雁叫西風。
感謝支持!
                                        ---起個名忒難

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、流的概念和作用。 流是一種有順序的,有起點和終點的字節集合,是對數據傳輸的總成或抽象。即數據在兩設備之間的傳輸...
    布魯斯不吐絲閱讀 10,100評論 2 95
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 朋友說,戀愛的捷徑,是有一個好的備胎。惡補了好多瑪麗蘇劇情才發現,只有綠茶婊才能輕松應對八九十來個備胎,顯然,我不...
    木子魚兒閱讀 431評論 1 0
  • 周日上午在家,孩子說作業寫完了,想看手機視頻半小時,因為昨天能說到做到,就把手機給了他。結果是看了一個小時才把手機...
    聽雨聆聲閱讀 249評論 0 7