C語言中文網 目錄

Java字節流的使用:字節輸入/輸出流、文件輸入/輸出流、字節數組輸入/輸出流

在本章的第一節《Java什么是輸入/輸出流?》中就提到 Java 所有表示字節輸入流類的父類是 InputStream,它是一個抽象類,因此繼承它的子類要重新定義父類中的抽象方法。所有表示字節輸出流類的父類是 OutputStream,它也是一個抽象類,同樣子類需要重新定義父類的抽象方法。

下面首先介紹上述兩個父類提供的常用方法,然后介紹如何使用它們的子類輸入和輸出字節流,包括 ByteArrayInputStream 類、ByteArrayOutputStream 類、FileInputStream 類和 FileOutputStream 類。

字節輸入流

InputStream 類及其子類的對象表示一個字節輸入流。

InputStream 類的常用子類如下。
  • ByteArrayInputStream 類:將字節數組轉換為字節輸入流,從中讀取字節。
  • FileInputStream 類:從文件中讀取數據。
  • PipedInputStream 類:連接到一個 PipedOutputStream(管道輸出流)。
  • SequenceInputStream 類:將多個字節輸入流串聯成一個字節輸入流。
  • ObjectInputStream 類:將對象反序列化。

使用 InputStream 類的方法可以從流中讀取一個或一批字節。表 1 列出了 InputStream 類的常用方法。

表1 InputStream類的常用方法
方法名及返回值類型 說明
int read() 從輸入流中讀取一個 8 位的字節,并把它轉換為 0~255 的整數,最后返 回整數。
如果返回 -1,則表示已經到了輸入流的末尾。為了提高 I/O 操作的效率,建議盡量
使用 read() 方法的另外兩種形式
int read(byte[] b) 從輸入流中讀取若干字節,并把它們保存到參數 b 指定的字節數組中。 該方法返回
讀取的字節數。如果返回 -1,則表示已經到了輸入流的末尾
int read(byte[] b, int off, int len) 從輸入流中讀取若干字節,并把它們保存到參數b指定的字節數組中。其中,off 指
定在字節數組中開始保存數據的起始下標;len 指定讀取的字節數。該方法返回實際
讀取的字節數。如果返回 -1,則表示已經到了輸入流的末尾
void close() 關閉輸入流。在讀操作完成后,應該關閉輸入流,系統將會釋放與這個輸入流相關
的資源。注意,InputStream 類本身的 close() 方法不執行任何 操作,但是它的許多
子類重寫了 close() 方法
int available() 返回可以從輸入流中讀取的字節數
long skip(long n) 從輸入流中跳過參數n指定數目的字節。該方法返回跳過的字節數
void mark(int readLimit) 在輸入流的當前位置開始設置標記,參數 readLimit 則指定了最多被設置 標記的字
節數
boolean markSupported() 判斷當前輸入流是否允許設置標記,是則返回 true,否則返回 false
void reset() 將輸入流的指針返回到設置標記的起始處

注意:在使用 mark() 方法和 reset() 方法之前,需要判斷該文件系統是否支持這兩個方法,以避免對程序造成影響。

字節輸出流

OutputStream 類及其子類的對象表示一個字節輸出流。OutputStream 類的常用子類如下。
  • ByteArrayOutputStream 類:向內存緩沖區的字節數組中寫數據。
  • FileOutputStream 類:向文件中寫數據。
  • PipedOutputStream 類:連接到一個 PipedlntputStream(管道輸入流)。
  • ObjectOutputStream 類:將對象序列化。

利用 OutputStream 類的方法可以從流中寫入一個或一批字節。表 2 列出了 OutputStream 類的常用方法。

表2 OutputStream類的常用方法
方法名及返回值類型 說明
void write(int b) 向輸出流寫入一個字節。這里的參數是 int 類型,但是它允許使用表達式,
而不用強制轉換成 byte 類型。為了提高 I/O 操作的效率,建議盡量使用
write() 方法的另外兩種形式
void write(byte[] b) 把參數 b 指定的字節數組中的所有字節寫到輸出流中
void write(byte[] b,int off,int len) 把參數 b 指定的字節數組中的若干字節寫到輸出流中。其中,off 指定字節
數組中的起始下標,len 表示元素個數
void close() 關閉輸出流。寫操作完成后,應該關閉輸出流。系統將會釋放與這個輸出
流相關的資源。注意,OutputStream 類本身的 close() 方法不執行任何操
作,但是它的許多子類重寫了 close() 方法
void flush() 為了提高效率,在向輸出流中寫入數據時,數據一般會先保存到內存緩沖
區中,只有當緩沖區中的數據達到一定程度時,緩沖區中的數據才會被寫
入輸出流中。使用 flush() 方法則可以強制將緩沖區中的數據寫入輸 出流,
并清空緩沖區

字節數組輸入流

ByteArrayInputStream 類可以從內存的字節數組中讀取數據,該類有如下兩種構造方法重載形式。
  1. ByteArrayInputStream(byte[] buf):創建一個字節數組輸入流,字節數組類型的數據源由參數 buf 指定。
  2. ByteArrayInputStream(byte[] buf,int offse,int length):創建一個字節數組輸入流,其中,參數 buf 指定字節數組類型的數據源,offset 指定在數組中開始讀取數據的起始下標位置,length 指定讀取的元素個數。

例 1

使用 ByteArrayInputStream 類編寫一個案例,實現從一個字節數組中讀取數據,再轉換為 int 型進行輸出。代碼如下:
package ch13;
import java.io.ByteArrayInputStream;
public class test08
{
    public static void main(String[] args)
    {
        byte[] b=new byte[]{1,-1,25,-22,-5,23};    //創建數組
        ByteArrayInputStream bais=new ByteArrayInputStream(b,0,6);    //創建字節數組輸入流
        int i=bais.read();    //從輸入流中讀取下一個字節,并轉換成int型數據
        while(i!=-1)
        {    //如果不返回-1,則表示沒有到輸入流的末尾
            System.out.println("原值="+(byte)i+"\t\t\t轉換為int類型="+i);
            i=bais.read();    //讀取下一個
        }
    }
}
在該示例中,字節輸入流 bais 從字節數組 b 的第一個元素開始讀取 4 字節元素,并將這 4 字節轉換為 int 類型數據,最后返回。

提示:上述示例中除了打印 i 的值外,還打印出了 (byte)i 的值,由于 i 的值是從 byte 類型的數據轉換過來的,所以使用 (byte)i 可以獲取原來的 byte 數據。

該程序的運行結果如下:
原值=1            轉換為int類型=1
原值=-1            轉換為int類型=255
原值=25            轉換為int類型=25
原值=-22            轉換為int類型=234
原值=-5            轉換為int類型=251
原值=23            轉換為int類型=23

從上述的運行結果可以看出,字節類型的數據 -1 和 -22 轉換成 int 類型的數據后變成了 255 和 234,對這種結果的解釋如下:
  • 字節類型的 1,二進制形式為 00000001,轉換為 int 類型后的二進制形式為 00000000 00000000 0000000000000001,對應的十進制數為 1。
  • 字節類型的 -1,二進制形式為 11111111,轉換為 int 類型后的二進制形式為 00000000 00000000 0000000011111111,對應的十進制數為 255。

可見,從字節類型的數轉換成 int 類型的數時,如果是正數,則數值不變;如果是負數,則由于轉換后,二進制形式前面直接補了 24 個 0,這樣就改變了原來表示負數的二進制補碼形式,所以數值發生了變化,即變成了正數。

提示:負數的二進制形式以補碼形式存在,例如 -1,其二進制形式是這樣得來的:首先獲取 1 的原碼 00000001,然后進行反碼操作,1 變成 0,0 變成 1,這樣就得到 11111110,最后進行補碼操作,就是在反碼的末尾位加 1,這樣就變成了 11111111。

字節數組輸出流

ByteArrayOutputStream 類可以向內存的字節數組中寫入數據,該類的構造方法有如下兩種重載形式。
  1. ByteArrayOutputStream():創建一個字節數組輸出流,輸出流緩沖區的初始容量大小為 32 字節。
  2. ByteArrayOutputStream(int size):創建一個字節數組輸出流,輸出流緩沖區的初始容量大小由參數 size 指定。

ByteArrayOutputStream 類中除了有前面介紹的字節輸出流中的常用方法以外,還有如下兩個方法。
  1. intsize():返回緩沖區中的當前字節數。
  2. byte[] toByteArray():以字節數組的形式返回輸出流中的當前內容。

例 2

使用 ByteArrayOutputStream 類編寫一個案例,實現將字節數組中的數據輸出,代碼如下所示。
package ch13;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
public class Test09
{
    public static void main(String[] args)
    {
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        byte[] b=new byte[]{1,-1,25,-22,-5,23};    //創建數組
        baos.write(b,0,6);    //將字節數組b中的前4個字節元素寫到輸出流中
        System.out.println("數組中一共包含:"+baos.size()+"字節");    //輸出緩沖區中的字節數
        byte[] newByteArray=baos.toByteArray();    //將輸出流中的當前內容轉換成字節數組
        System.out.println(Arrays.toString(newByteArray));    //輸出數組中的內容
    }
}

該程序的輸出結果如下:
數組中一共包含:6字節
[1, -1, 25, -22, -5, 23]

文件輸入流

FileInputStream 是 Java 流中比較常用的一種,它表示從文件系統的某個文件中獲取輸入字節。通過使用 FileInputStream 可以訪問文件中的一個字節、一批字節或整個文件。

在創建 FileInputStream 類的對象時,如果找不到指定的文件將拋出 FileNotFoundException 異常,該異常必須捕獲或聲明拋出。

FileInputStream 常用的構造方法主要有如下兩種重載形式。
  1. FileInputStream(File file):通過打開一個到實際文件的連接來創建一個 FileInputStream,該文件通過文件系統中的 File 對象 file 指定。
  2. FileInputStream(String name):通過打開一個到實際文件的鏈接來創建一個 FileInputStream,該文件通過文件系統中的路徑名 name 指定。

下面的示例演示了 FileInputStream() 兩個構造方法的使用。
try
{
    //以File對象作為參數創建FileInputStream對象
    FileInputStream fis1=new FiieInputStream(new File("F:/mxl.txt"));
    //以字符串值作為參數創建FilelnputStream對象
    FileInputStream fis2=new FileInputStream("F:/mxl.txt");
}
catch(FileNotFoundException e)
{
    System.out.println("指定的文件找不到!");
}

例 3

假設有一個 E:\myjava\HelloJava.java 文件,下面使用 FileInputStream 類讀取并輸出該文件的內容。具體代碼如下:
package ch13;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Test10
{
    public static void main(String[] args)
    {
        File f=new File("E:/myjava/HelloJava.java");
        FileInputStream fis=null;
        try
        {
            //因為File沒有讀寫的能力,所以需要有個InputStream
            fis=new FileInputStream(f);
            //定義一個字節數組
            byte[] bytes=new byte[1024];
            int n=0;    //得到實際讀取到的字節數
            System.out.println("E:\\myjava\\HelloJava.java文件內容如下:");
            //循環讀取
            while((n=fis.read(bytes))!=-1)
            {
                String s=new String(bytes,0,n);    //將數組中從下標0到n的內容給s
                System.out.println(s);
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                fis.close();
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}

如上述代碼,在 FileInputDemo 類的 main() 方法中首先創建了一個 File 對象 f,該對象指向 E:\myjava\HelloJava.java 文件。接著使用 FileInputStream 類的構造方法創建了一個 FileInputStream 對象 fis,并聲明一個長度為 1024 的 byte 類型的數組,然后使用 FileInputStream 類中的 read() 方法將 HelloJava.java 文件中的數據讀取到字節數組 bytes 中,并輸出該數據。最后在 finally 語句中關閉 FileInputStream 輸入流。

圖 1 所示為 HelloJava.java 文件的原始內容,如下所示的是運行程序后的輸出內容。
D:\myjava\HelloJava.java文件內容如下:
/*
*第一個Java程序
*/
public class HelloJava{
    //這里是程序入口
    public static void main(String[] args){
        //輸出字符串
        System.out.println("你好 Java");
    }
}


圖1 HelloJava.java文件內容

注意:FileInputStream 類重寫了父類 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法,不支持 mark() 方法和 reset() 方法。

文件輸出流

FileOutputStream 類繼承自 OutputStream 類,重寫和實現了父類中的所有方法。FileOutputStream 類的對象表示一個文件字節輸出流,可以向流中寫入一個字節或一批字節。在創建 FileOutputStream 類的對象時,如果指定的文件不存在,則創建一個新文件;如果文件已存在,則清除原文件的內容重新寫入。

FileOutputStream 類的構造方法主要有如下 4 種重載形式。
  1. FileOutputStream(File file):創建一個文件輸出流,參數 file 指定目標文件。
  2. FileOutputStream(File file,boolean append):創建一個文件輸出流,參數 file 指定目標文件,append 指定是否將數據添加到目標文件的內容末尾,如果為 true,則在末尾添加;如果為 false,則覆蓋原有內容;其默認值為 false。
  3. FileOutputStream(String name):創建一個文件輸出流,參數 name 指定目標文件的文件路徑信息。
  4. FileOutputStream(String name,boolean append):創建一個文件輸出流,參數 name 和 append 的含義同上。

注意:使用構造方法 FileOutputStream(String name,boolean append) 創建一個文件輸出流對象,它將數據附加在現有文件的末尾。該字符串 name 指明了原文件,如果只是為了附加數據而不是重寫任何已有的數據,布爾類型參數 append 的值應為 true。

對文件輸出流有如下四點說明:
  1. 在 FileOutputStream 類的構造方法中指定目標文件時,目標文件可以不存在。
  2. 目標文件的名稱可以是任意的,例如 F:\\abc、F:\\abc.de 和 F:\\abC.de.fg 等都可以,可以使用記事本等工具打開并瀏覽這些文件中的內容。
  3. 目標文件所在目錄必須存在,否則會拋出 java.io.FileNotFoundException 異常。
  4. 目標文件的名稱不能是已存在的目錄。例如F盤下已存在java文件夾,那么就不能使用 java 作為文件名,即不能使用 F:\\java,否則拋出 java.io.FileNotFoundException 異常。

例 4

同樣是讀取 E:\myjava\HelloJava.java 文件的內容,在這里使用 FileInputStream 類實現,然后再將內容寫入新的文件 E:\myjava\HelloJava.txt 中。具體的代碼如下:
package ch13;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test11
{
    public static void main(String[] args)
    {
        FileInputStream fis=null;    //聲明FileInputStream對象fis
        FileOutputStream fos=null;    //聲明FileOutputStream對象fos
        try
        {
            File srcFile=new File("E:/myjava/HelloJava.java");
            fis=new FileInputStream(srcFile);    //實例化FileInputStream對象
            File targetFile=new File("E:/myjava/HelloJava.txt");    //創建目標文件對象,該文件不存在
            fos=new FileOutputStream(targetFile);    //實例化FileOutputStream對象
            byte[] bytes=new byte[1024];    //每次讀取1024字節
            int i=fis.read(bytes);
            while(i!=-1)
            {
                fos.write(bytes,0,i);    //向E:\newStudent.txt文件中寫入內容
                i=fis.read(bytes);
            }
            System.out.println("寫入結束!");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                fis.close();    //關閉FileInputStream對象
                fos.close();    //關閉FileOutputStream對象
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}

如上述代碼,將 E:\myjava\HelloJava.java 文件中的內容通過文件輸入/輸出流寫入到了 E:\myjava\HelloJava.txt 文件中。由于 HelloJava.txt 文件并不存在,所以在執行程序時將新建此文件,并寫入相應內容。

運行程序,成功后會在控制臺輸出“寫入結束!”。此時,打開 E:\myjava\HelloJava.txt 文件會發現,其內容與 HelloJava.java 文件的內容相同,如圖 2 所示。


圖2

技巧:在創建 FileOutputStream 對象時,如果將 append 參數設置為 true,則可以在目標文件的內容末尾添加數據,此時目標文件仍然可以暫不存在。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo