Java輸入輸出流(一)

Java輸入輸出流(一)#


Android 是基于 Java 語言編寫的,在安卓程序的很多時候會用到有關 I/O 操作,要在 Adroid 中使用 I/O 操作就必須學會 JavaI/O 操作。

首先要知道的是,Java 中輸入和輸出的概念都是對于程序來說的。就是 外部往程序輸入,程序向外部輸出。外部往程序輸入,則程序必須去 取外部,程序向外部輸出,則程序必須去 給外部。在外部和程序之間,Java 分別通過 輸入流InputStream輸出流OutStream 進行聯(lián)系。

流的示意圖如下

Java.io 的結構樹##

本人才疏學淺,對于Java I/O也是初學階段,因此本著與讀者一同探討的心態(tài),按照結構樹從上至下逐個去看一下每一個類的使用方法。

字節(jié)流與字符流##

從結構樹我們可以看到位于樹的頂端是字節(jié)流和字符流。

  1. 字節(jié)流 :表示以字節(jié)為單位從 stream 中讀取或往 stream 中寫入信息,即 io 包中的 inputstream 類和 outputstream 類的派生類。通常用來讀取二進制數(shù)據(jù),如圖象和聲音。
  2. 字符流 :以 Unicode 字符為導向的 stream,表示以 Unicode 字符為單位從 stream 中讀取或往 stream 中寫入信息。 區(qū)別: ReaderWriter 要解決的,最主要的問題就是國際化。原先的 I/O 類庫只支持8位的字節(jié)流,因此不可能很好地處理16位的 Unicode 字符流。Unicode 是國際化的字符集(更何況 Java 內(nèi)置的 char 就是16位的 Unicode 字符),這樣加了 ReaderWriter 之后,所有的 I/O 就都支持 Unicode 了。

InputStream與OutputStream##

對于字節(jié)流,它有兩個抽象類:InputStreamOutputStream,在這兩個抽象類里面沒有具體的實現(xiàn)方法,它的具體實現(xiàn)需要它的 子類 來實現(xiàn),在它里面定義了一些函數(shù)用以 子類 的去實現(xiàn)。

1 InputStream###

我們打開 InputStream 源碼看到里面定義一些方法:

  • public abstract int read() :讀取 input stream 的下一個字節(jié),返回值為下一個字節(jié)
  • public int read(byte b[]) : 讀取 input stream ,并將其存在數(shù)組 b 中,以b[0]開始存儲,最多能讀取 b.length 個字節(jié)
  • public int read(byte b[], int off, int len) :讀取 input stream ,讀取的長度為 len ,以 b[off] 開始存儲,read(byte b[]) 的實現(xiàn)就是調用該方法: read(b,0,b.length)
  • public long skip(long n) :跳過 input stream 的 n 個字節(jié)
  • public int available() :粗略估計 input stream 可以讀取的字節(jié),返回估計值,不一定準確。不會阻塞調用 input stream 的下一個方法
  • public void close() : 關閉 input stream, 釋放與input stream 相關的系統(tǒng)資源
  • public synchronized void mark(int readlimit) :標記input stream當前 (mark()被調用的這一刻 )位置,參數(shù) readlimit 用來告訴系統(tǒng),當之后讀取的字節(jié)數(shù)超過 readlimit 后,mark 失效
  • public synchronized void reset() :退回到最后一次 input stream 調用 mark()方法地方,之后讀取字節(jié)的時候就會從 mark 處開始。當 mark() 調用的時候 input stream 就會記住從這一刻開始讀入的所有字節(jié),當 reset() 被調用的時候,就會準備好提供這些字節(jié),而如果讀入的字節(jié)數(shù)超過了 readlimit,reset()還沒有被調用,那么 input stream 就不會繼續(xù)記錄,也就是 mark 失效了
  • public boolean markSupported() :測試該 IO 類是否支持 mark() 和 reset()方法

2 OutputStream###

同樣我們打開 OutputStream 的源碼,可以看到里面有這些方法:

  • public abstract void write(int b) :往 output stream 寫出一個字節(jié),這個字節(jié)是 b 的低八位,b 的高24位會被忽略
  • public void write(byte b[]) :往 output stream 寫出 b.length 長度的字節(jié),通過調用 write( b, 0, b.length) 來實現(xiàn)
  • public void write(byte b[], int off, int len) :寫出長度為 len 的字節(jié),第一個寫出的是 b[off],
  • public void flush() :清空當前 output stream以及強制輸出所有緩沖數(shù)據(jù)
  • public void close() :關閉 output stream, 釋放與input stream 相關的系統(tǒng)資源

InputStreamOutputStream 兩個抽象類定義了輸入流和輸出流應該具備什么功能,但是具體卻沒有實現(xiàn)。她們的具體實現(xiàn)是通過繼承于它們的子類來實現(xiàn)的

1.1 FileInputStream###

對文件進行寫入的類。對 file 類沒有了解的讀者可以查閱相關資料,這里給出一個網(wǎng)址以供產(chǎn)考:

https://zhayh.gitbooks.io/java/content/ch11_file_io/1_1_file_class.html

首先看看它的構造方法:

  • FileInputStream(File file) :通過傳入一個 file 類對象,來創(chuàng)建 file input stream
  • FileInputStream(FileDescriptor fdObj) :通過 file descriptor fdObj 來創(chuàng)建一個 file input stream
  • FileInputStream(String name) :通過傳入一個 file 在系統(tǒng)中的名字(或者路徑)來創(chuàng)建 file input stream,實際上他是通過調用 FileInputStream(name != null ? new File(name) : null) 來實現(xiàn)的

再來看看它的方法:

  • int available() :粗略估計 input stream 可以讀取的字節(jié),返回估計值,不一定準確。不會阻塞調用 input stream 的下一個方法
  • void close() :關閉 input stream ,釋放與之相關的系統(tǒng)資源
  • protected void finalize() :確保 input stream 已經(jīng)關閉如果沒有,則繼續(xù)調用 close()
  • int read() :讀取 input stream 的下一個字節(jié)。這個方法是Java的原始類,底層是用C/C++來實現(xiàn)的
  • int read(byte[] b) :讀取 b.length 長度的字節(jié)并存到 b 中
  • int read(byte[] b, int off, int len) :讀取 input stream ,讀取的長度為 len ,以 b[off] 開始存儲,read(byte b[]) 的實現(xiàn)就是調用該方法: read(b,0,b.length)
  • long skip(long n) :跳過 input stream 的 n 個字節(jié)

代碼舉例:

FileInputStream fileInput = null;
    byte [] b = new byte[2097152];//用來存放讀取輸入流的數(shù)據(jù)
    File file = new File("C:/Users/加鹽/Desktop/logo.jpg");
    try {                                     //try catch 是用來捕獲異常
        fileInput = new FileInputStream(file);//通過傳遞file參數(shù)來新建file input stream 對象
    } catch (FileNotFoundException e) {       
        e.printStackTrace();
    }        
    try {
        fileInput.read(b); //讀取數(shù)據(jù),并存到b中
        fileInput.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

要注意的是在 windows 和 Linux 系統(tǒng)中,"/"表示分割符,在 windows 中還可以用 "",但是由于java中 ""表示轉義符號,因此要用 "\",例如:C:\\Users\\加鹽\\Desktop\\logo.jpg

1.1.1 BufferedInputStream###

如果傳輸?shù)奈募容^大,直接利用 FileInputStream 時,是程序直接與硬盤進行訪問,我們知道硬盤的存取速度是最慢的,因此這樣就會比較耗時。如果我們先把要存儲的數(shù)據(jù)放到內(nèi)存中,等到內(nèi)存區(qū)滿了之后,我們再把內(nèi)存的數(shù)據(jù)寫到硬盤中,這樣速度就會快很多。因為首先計算機對內(nèi)存的訪問速度比訪問硬盤要快,其次,由于是每次指定的內(nèi)存區(qū)滿了之后,再把數(shù)據(jù)寫到硬盤,這樣就減少了對硬盤的訪問次數(shù),節(jié)省了很多時間。

BufferedInputStream 又稱為包裝流,因為它能將其他的 InputStream 封裝成 BufferedInputStream,這一點從它的構造函數(shù)就可以看到:

BufferedInputStream(InputStream in) :創(chuàng)建一個 BufferedInputStream ,并把它的參數(shù) in 保存起來,用以后續(xù)的使用
BufferedInputStream(InputStream in, int size) :創(chuàng)建一個BufferedInputStream ,并把它的參數(shù) in 保存起來,用以后續(xù)的使用,而且 size 指定了這個緩沖區(qū)的大小,也就是輸入設備一次往緩沖區(qū)寫入 size 字節(jié)大小的數(shù)據(jù)。待程序讀取完,緩沖區(qū)為空后,再次輸入設備再向緩沖區(qū)寫入數(shù)據(jù)。若沒有指定 size ,系統(tǒng)默認為8192。

它的方法:

  • int available()
  • void close()
  • void mark(int readlimit)
  • boolean markSupported()
  • int read()
  • int read(byte[] b, int off, int len)
  • void reset()
  • long skip(long n)

以上所有的方法與前面所講的 InputStream 類的同名方法的作用一樣的,下面來看例子:

在IO.txt中


現(xiàn)在利用 BufferedInputStream(InputStream in) 將它讀入 ,并顯示到控制臺

public static void main(String[] arg)
FileInputStream fileInput = null;
    BufferedInputStream bufferInput = null;
    int len = 0;
    byte [] b = new byte[1024];
    File file = new File("C:/Users/加鹽/Desktop/IO.txt");
    try {
        fileInput = new FileInputStream(file);
        bufferInput = new BufferedInputStream(fileInput); //把FileInputStream包裝成 BufferInputStream
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }        
    try {
        len = bufferInput.read(b);
        bufferInput.close();
        fileInput.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    for(int i=0;i<len;i++){
        System.out.print((char) b[i]+"");
    }
}
}


運行結果如上圖所示

現(xiàn)在我們來用一下 mark()方法:

public static void main(String[] arg) throws IOException{
FileInputStream fileInput = null;
    BufferedInputStream bufferInput = null;
    int len = 0;
    byte [] b = new byte[1024];
    File file = new File("C:/Users/加鹽/Desktop/IO.txt");
    fileInput = new FileInputStream(file);
    bufferInput = new BufferedInputStream(fileInput);
    bufferInput.read(b, 0, 3);//先讓bufferInput被讀取3個字節(jié)
    bufferInput.mark(10);     //然后進行標記
    for(int i = 1;i<10;i++){
        int a = 3*i;
        bufferInput.read(b,a,3); //然后在往后讀取三個字節(jié)后
        bufferInput.reset();     //然后重置,也就是回到bufferInput的被標記的位置
    }                            //下一次讀取bufferInput的時候又從標記點開始
                                 //所以我們可以猜想,最后一定會第四個字節(jié)和第六個字節(jié)重復出現(xiàn)九次  
    bufferInput.close(); 
    fileInput.close();
    for(int i=0;i<b.length;i++){
        if(b[i]==0)break;
        System.out.print((char) b[i]+"");            
    }
}
}


運行結果如上圖,符合我們的猜想

1.1.2 DataInputStream###

前面我們看到,進入 input stream 的基本單位都是字節(jié),盡管底層確實是以字節(jié)作為傳輸單位,但是有些時候就顯得不太方便,,比如說我知道要傳輸?shù)亩际?int 類型的數(shù)據(jù),那么我們是否可以直接以 int 類型的數(shù)據(jù)進行傳輸單位呢?

DataInputStream 類就解決了這個問題,允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數(shù)據(jù)類型。應用程序可以使用數(shù)據(jù)輸出流寫入稍后由數(shù)據(jù)輸入流讀取的數(shù)據(jù)(請稍微注意這一句話)。

下面來看它的構造函數(shù):

  • DataInputStream(InputStream in) :通過傳入 InputStream 來構造 DataInputstream

方法:

  • int read(byte[] b) :與前面 InputStream 的同名方法一樣
  • int read(byte[] b, int off, int len) :與前面 InputStream 的同名方法一樣
  • boolean readBoolean() :讀取一個字節(jié),若該字節(jié)為非零,則返回 true ,若為零,則返回 false
  • byte readByte() :讀取一個字節(jié)
  • char readChar() :讀取兩個字節(jié)并返回一個 char 類型數(shù)據(jù)
  • double readDouble() :讀取八個字節(jié)并返回一個 double 類型的數(shù)據(jù)
  • float readFloat() :讀取四位字節(jié)并返回一個 float 類型的數(shù)據(jù)
  • void readFully(byte[] b) :與 int read(byte[] b) 類似,只是沒有返回值
  • void readFully(byte[] b, int off, int len) :與int read(byte[] b, int off, int len)類似,返回值為零,從源碼中可以看到,這個方法有一個機制保證一定能讀取到 len 長度的字節(jié)數(shù)
  • int readInt() :讀取四個字節(jié)并返回一個 int 類型的數(shù)據(jù)
  • long readLong() :讀取八個字節(jié),并返回一個 long 類型的數(shù)據(jù)
  • short readShort() :讀取兩個字節(jié),并返回一個 short 類型的數(shù)據(jù)
  • int readUnsignedByte() :讀取一個字節(jié),返回一個無符號類型的字節(jié),實際上就是把讀取到的數(shù)據(jù)轉成 int 類型輸出
  • int readUnsignedShort() :讀取梁個字節(jié),返回一個無符號類型的 short 數(shù)據(jù),實際上就是把讀取到的數(shù)據(jù)轉成 int 類型輸出
  • String readUTF() :讀取用UTF-8編碼的字符串
  • int skipBytes(int n) :跳過 n 個字節(jié)

下面我們來看例子:

    public static void main(String[] arg) throws IOException{
    FileInputStream fileInput = null;
    DataInputStream dataInput = null;
    byte [] b = new byte[1024];
    File file = new File("C:/Users/加鹽/Desktop/IO.txt");
    fileInput = new FileInputStream(file);
    dataInput = new DataInputStream(fileInput);
    System.out.print(dataInput.readBoolean());
    dataInput.close();
    fileInput.close();
}
}

一個簡單的例子,從輸入流中讀取一個字節(jié),并在控制臺輸出:輸入流在文件中讀取一個字符“6”,底層變成相應的字節(jié),然后將文件輸入流包裝成數(shù)據(jù)輸入流,調用數(shù)據(jù)輸入流讀取這個字節(jié),返回一個 Boolean 類型的值


更多的方法不一一展示,讀者可自行回去嘗試,值得一提的是,。通過查看 DataInputStream 類的源碼我們可以發(fā)現(xiàn),對于返回不同數(shù)據(jù)類型的方法,其實現(xiàn)方法大體相同:

  1. 首先調用 read() 方法,read()的方法讀取一個字節(jié),然后為高位補零,返回一個 int 類型值
  2. 根據(jù)數(shù)據(jù)類型的所占用的字節(jié)數(shù),相應調用幾次 read()方法
  3. 然后進行移位操作,最后把幾個字節(jié)拼接起來,返回對應的數(shù)據(jù)類型

下面以 readShort()為例,為大家演示一下,假設底下的字節(jié)數(shù)為無符號數(shù),并且以原碼形式存放(底下并非如此,但是為了方便說明,故如此假設)

代碼:

public final short readShort() throws IOException {
    int ch1 = in.read();
    int ch2 = in.read();   //如果到了文件尾返回-1
    if ((ch1 | ch2) < 0)   //如果如果沒有讀夠兩個字節(jié),則拋出異常
        throw new EOFException();
    return (short)((ch1 << 8) + (ch2 << 0));//第一個字節(jié)(實際上是一個高24為0的
                                            //int)左移動8位成為高8位,節(jié)不動,   
                                            //然后拼接成為一個新數(shù)據(jù),在把它強制
                                            //類型轉換變成 short 類型
}

假設輸入流有兩個字節(jié)為:00000001和00000002,那么ch1就為00000000000000000000000000000001,ch2為00000000000000000000000000000001
,ch1左移8位:00000000000000000000000100000000,最后拼接起來就是:00000000000000000000000100000002,強制類型轉換后變成0000000100000002

2.1 FileOutputStream###

2.1.1 BufferedOutputStream###

2.1.2 DataOutputStream###

2.1.3 PrintStream###

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內(nèi)部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,626評論 18 399
  • 一、流的概念和作用。 流是一種有順序的,有起點和終點的字節(jié)集合,是對數(shù)據(jù)傳輸?shù)目偝苫虺橄?。即?shù)據(jù)在兩設備之間的傳輸...
    布魯斯不吐絲閱讀 10,310評論 2 95
  • 近幾天找來李碧華的《青蛇》看。沒讀多少,看到青蛇和白蛇的對話,有一段是這樣寫的: 晚上,我倆自湖底出來,吸收青煙紫...
    周YAJING閱讀 1,800評論 1 4
  • 菱角分明,好壞不分。歲月靜好,不知安分。沐浴在雞湯的大澡盆中,排泄的是每天的安穩(wěn)。 今天是大二的最后一天,寢...
    王汽水閱讀 274評論 0 0

友情鏈接更多精彩內(nèi)容