天天看點

【IO流】位元組輸入輸出流

1. 位元組流架構

【IO流】位元組輸入輸出流

【IO流】位元組輸入輸出流

2. InputOutputStreanm 位元組輸入輸出流

  • 位元組流類用于向位元組流讀寫8位二進制的位元組,一般的,位元組流類主要用于讀寫諸如圖像或聲音等多媒體的二進制資料
  • 位元組流類以InputStream和OutputStream為基類,它們都是抽象類,但是有非抽象方法
  • InputStream方法摘要:
    • public abstract int read():唯一的抽象方法,從輸入流中讀取資料的下一個位元組
    • public int read(byte[] b):從輸入流中讀取一定數量的位元組,并将其存儲在緩沖區數組b中
    • public int read(byte[] b, int off, int len):将輸入流中從off開始,最多len個資料位元組存入緩沖區資料b中
    • public long skip(long n):跳過和丢棄此輸入流中資料的n個位元組
    • public void close():關閉此輸入流并釋放與該流相關的所有系統資源
    • public void reset():将此流重新定位到最後一次對此輸入流調用mark方法時的位置
  • OutputStream方法摘要:
    • public abstract void write(int b):唯一的抽象方法,将制定的位元組寫入此輸出流
    • public void write(byte[] b):将b.length個位元組從指定的byte數組寫入此輸出流。
    • public void write(byte[] b, int off, int len):将制定byte數組中從偏移量off開始的len個位元組寫入此輸出流
    • public void flush():出阿信此輸出流并強制卸除所有緩沖的輸出位元組
    • public void clase():關閉此輸出流并釋放與該流相關的所有系統資源
  • OutputStream類所有方法無傳回值,在出錯情況下會抛出IOException異常。

3. FileInput/OutputStream:檔案輸入輸出流

  • FileInputStream類表示能從檔案擷取位元組的InputStream類
  • 構造方法:
    • FileInputStream(String filepath)
    • FileInputStream(File fileobj)
  • FileOutputStream類表示能向檔案寫入位元組的OutputStream類
  • 構造方法:
    • FileOutputStream(String filepath)
    • FileOutputStream(File fileobj)
    • FileOutputStream(String filepath, boolean append)
【IO流】位元組輸入輸出流

對位元組流進行讀寫操作時,我們通常都會将緩存數組傳入到read(),write()方法,而不是直接用無參的read(),write()方法一個位元組一個位元組的傳輸。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileInputStreamOutputStreamDemo {

     public static void main(String[] args) {
          try {
              //使用讀寫緩沖區對讓檔案的讀寫效率大大提高
              FileCopyUtil.copyFile(new File("d:\\JAVAstudy\\JavaTest\\11.txt"), new File("d:\\JAVAstudy\\JavaTest\\111.txt"));
              FileCopyWithCacheUtil.copyFileWithCache(new File("d:\\JAVAstudy\\JavaTest\\22.txt"), new File("d:\\JAVAstudy\\JavaTest\\222.txt"));
          } catch (IOException e) {
              e.printStackTrace();
          }
     }
}

//按位元組複制
class FileCopyUtil{
     public static void copyFile(File src,File dst)throws IOException{
          //把要複制的檔案讀取到檔案輸入流fis中
          FileInputStream fis=new FileInputStream(src);
          //把檔案輸出流fos輸出到檔案dst中
          FileOutputStream fos=new FileOutputStream(dst);
          long time1=System.currentTimeMillis();
          int data=-;
          //一個位元組一個位元組的傳,讀fis中的資料,賦給data,結束标志是-1
          //read()方法表示讀位元組,并傳回一個int類型的資料
          while(((data=fis.read())!=-)){
              fos.write(data);
          }
          //把輸入輸出位元組流關閉
          fis.close();
          fos.close();
          long time2=System.currentTimeMillis();
          System.out.println("複制完成,共花費:"+(time2-time1)+"ms");
     }
}

//帶緩存的複制
class FileCopyWithCacheUtil{
     public static void copyFileWithCache(File src,File dst)throws IOException{
          //把要複制的檔案讀取到檔案輸入流fis中
          FileInputStream fis=new FileInputStream(src);
          //把檔案輸出流fos輸出到檔案dst中
          FileOutputStream fos=new FileOutputStream(dst);
          //準備1M大小的緩沖區
          byte[] cache=new byte[*];
          long time1=System.currentTimeMillis();
          int len=;
          //帶緩存的read()方法傳回實際讀到的位元組數,以“-1”為結束标志
          while((len=fis.read(cache))!=-){
              //指定從0開始到實際讀到的位置,寫進fos
              fos.write(cache,,len);
          }
          //把輸入輸出位元組流關閉
          fis.close();
          fos.close();
          long time2=System.currentTimeMillis();
          System.out.println("複制完成,共花費:"+(time2-time1)+"ms");
     }
}
           

4. ByteArrayInput/OutputStream:位元組輸入輸出流

  • ByteArrayInputStream是把位元組數組當成源的輸入流
  • ByteArrayInputStream包含一個内部緩沖區,該緩沖區包含從流中讀取的位元組,内部計數器跟蹤read方法要提供的下一個位元組;
  • 關閉ByteArrayInputStream無效,此類中的方法在關閉此流都仍可以被調用,而不會産生任何IOException。
  • 構造方法:
    • ByteArrayInputStream(byte[] array):array就是源位元組數組
    • ByteArrayInputStream(byte[] array, int start. int numBytes)
  • ByteArrayOutputStream是把位元組數組當做目标輸出流
  • ByteArrayOutputStream()實作了一個輸出流,其中的資料被寫入一個byte數組(緩沖區),緩沖區會随着資料的不斷寫入而自動增長,可使用toByteArray()和toString()擷取資料;
  • 關閉ByteArrayOutputStream()無效,此類中的方法在關閉此流都仍可以被調用,而不會産生任何IOException。
  • 構造方法:
    • ByteArrayOutputStream() :使用無參的ByteArrayOutputStream會預設把緩存中的資料輸入到一個建立的byte數組中且預設數組長度32byte。
    • ByteArrayOutputStream(int numBytes) :輸入流帶有numBytes個byte的緩沖區
  • 字元輸入輸出和緩存都位于記憶體中,并沒有調用底層IO,是以close方法失效。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteArrayInputOutputStreamDemo {

     public static void main(String[] args) throws IOException{
          String str="Hello, Shanghai!";
          //用getByte()方法把字元串轉化為位元組數組作為輸入源
          ByteArrayInputStream bis=new ByteArrayInputStream(str.getBytes());
          int data=-;
          while((data=bis.read())!=-){
              System.out.print((char)data);
          }
          //bis.close();
          ByteArrayOutputStream bos=new ByteArrayOutputStream();
          //wrtie()方法傳入int型的資料b被強制轉換成byte并存入緩沖區
          bos.write();//字元a的ASCII碼
          bos.write();//字元A的ASCII碼
          bos.write("Hello,World".getBytes());
          byte[] buffer=bos.toByteArray();
          for(byte b:buffer){
              System.out.print((char)b);
          }
          FileOutputStream fos=new FileOutputStream("d:\\aa.txt",true);
          bos.writeTo(fos);//把ByteArrayOutputStream資料寫到對應檔案輸出流中
          fos.close();
     }
}
           

參觀源代碼:

ByteArrayInputStream

package java.io;

public class ByteArrayInputStream extends InputStream {

    protected byte buf[]; //ByteArrayInputStream類裡面隐藏了一個緩沖區
    protected int pos; //ByteArrayInputStream讀到的位置,初始值是0
    protected int mark = ;
    protected int count;

    //傳入緩沖區的構造方法
    public ByteArrayInputStream(byte buf[]) {
        this.buf = buf;
        this.pos = ;
        this.count = buf.length;
    }

    //傳入緩沖區,偏移量,字元長度的構造方法
    public ByteArrayInputStream(byte buf[], int offset, int length) {
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.mark = offset;
    }

    //同步的read()方法
    public synchronized int read() {
        //還沒有讀完,就從buf中繼續取資料(&0xff)并傳回,否則就傳回-1
        return (pos < count) ? (buf[pos++] & ) : -;
    }

    //同步的帶參read()方法
    public synchronized int read(byte b[], int off, int len) {
        if (b == null) {
            throw new NullPointerException();//沒有傳入緩沖區,抛出非受查異常
        } else if (off <  || len <  || len > b.length - off) {
            throw new IndexOutOfBoundsException();//偏移量的長度數值違法,抛出該異常
        }

        if (pos >= count) {
            return -;//讀指針到達末尾,結束方法
        }

        int avail = count - pos;
        if (len > avail) {
            len = avail;
        }
        if (len <= ) {
            return ;
        }
        System.arraycopy(buf, pos, b, off, len);把buf中的資料複制到len中
        pos += len;
        return len;
    }


    public synchronized long skip(long n) {
        long k = count - pos;
        if (n < k) {
            k = n <  ?  : n;
        }

        pos += k;
        return k;
    }


    public synchronized int available() {
        return count - pos;
    }


    public boolean markSupported() {
        return true;
    }


    public void mark(int readAheadLimit) {
        mark = pos;
    }


    public synchronized void reset() {
        pos = mark;
    }

    //這裡close()方法沒有任何執行操作
    public void close() throws IOException {
    }

}
           

ByteArrayOutputStream

package java.io;

import java.util.Arrays;

public class ByteArrayOutputStream extends OutputStream {

    protected byte buf[];//位元組數組
    protected int count;//位元組數組中的寫入的資料位

    //無參的構造方法
    public ByteArrayOutputStream() {
        this(); //構造一個32位的位元組數組大小
    }

    //自定義位元組數組大小的構造方法
    public ByteArrayOutputStream(int size) {
        if (size < ) {
            throw new IllegalArgumentException("Negative initial size: "
                                               + size);
        }
        buf = new byte[size];
    }

    //確定緩沖區一直夠用
    private void ensureCapacity(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - buf.length > )
            grow(minCapacity);
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - ;

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = buf.length;
        int newCapacity = oldCapacity << ;
        if (newCapacity - minCapacity < )
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > )
            newCapacity = hugeCapacity(minCapacity);
        buf = Arrays.copyOf(buf, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < ) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

    //同步的無參write()方法,傳入int型的資料b被強制轉換成byte并存入緩沖區
    public synchronized void write(int b) {
        ensureCapacity(count + );
        buf[count] = (byte) b;
        count += ;
    }

    //同步的帶參數的write()方法,把緩沖區中的資料按照偏移量和長度寫入位元組數組buf中
    public synchronized void write(byte b[], int off, int len) {
        if ((off < ) || (off > b.length) || (len < ) ||
            ((off + len) - b.length > )) {
            throw new IndexOutOfBoundsException();
        }
        ensureCapacity(count + len);
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }

    //把ByteArrayOutputStream資料寫到對應檔案輸出流中
    public synchronized void writeTo(OutputStream out) throws IOException {
        out.write(buf, , count);
    }

    public synchronized void reset() {
        count = ;
    }

    //建立一個新配置設定的byte數組
    public synchronized byte toByteArray()[] {
        return Arrays.copyOf(buf, count);
    }

    public synchronized int size() {
        return count;
    }

    public synchronized String toString() {
        return new String(buf, , count);
    }

    public synchronized String toString(String charsetName)
        throws UnsupportedEncodingException
    {
        return new String(buf, , count, charsetName);
    }

    public synchronized String toString(int hibyte) {
        return new String(buf, hibyte, , count);
    }

    //這裡close()方法沒有任何執行操作
    public void close() throws IOException {
    }
}
           

5. FilterInput/OutputStream:過濾流

【IO流】位元組輸入輸出流
  • 不直接與檔案,裝置打交道,隻是對位元組流進行包裝處理通常使用的過濾流都是FilterInput/OutputStream的子類
  • 過濾流僅僅為底層透明地提供擴充功能的輸入輸出流的包裝。這些流一般由普通類的方法(即過濾流的父類通路)
  • FilterInputStream(InputStream is):過濾輸入流的構造方法
  • FilterOutputStream(OutputStream os):過濾輸出流的構造方法
  • 這些類提供的方法與Input/OutputStream類的方法相同

常用的過濾流

  • BufferedInput/OutputStream, 需要使用已經存在的節點流構造,提供帶緩沖的讀寫,提高了讀寫的效率
【IO流】位元組輸入輸出流
  • DataInput/OutputStream,資料輸入輸出流,允許應用程式讀寫基本的JAVA資料類型,應用程式可以使用資料輸出流寫入稍後由資料輸入流讀取,讀寫順序要一緻。

過濾流常用字段:

- byte[] buf :緩沖區數組

- int count:緩沖區有效位元組長度

- int marklimit:mark的最大極限

- int markpos:最後一次調用mark方法時的位元組位置

- int pos:緩沖區中目前追蹤位元組的位置

5.1 BufferedInput/OutputStream

  • BufferedInputStream 為另一個輸入流添加一些功能,即緩沖輸入以及支援 mark 和 reset 方法的能力。
  • 在建立 BufferedInputStream 時,會建立一個内部緩沖區數組。在讀取或跳過流中的位元組時,可根據需要從包含的輸入流再次填充該内部緩沖區,一次填充多個位元組。
  • void mark(int readlimit) 方法:記錄輸入流中的某個點,
  • void reset(long n) 方法:将此流重新定位到最後一次調用mark方法時的位置
  • BufferedOutputStream 該類實作緩沖的輸出流。通過設定這種輸出流,應用程式就可以将各個位元組寫入底層輸出流中,而不必針對每次位元組寫入調用底層系統。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedInputOutputStreamDemo {
     public static void main(String[] args) {
          try {
              //使用讀寫緩沖區對讓檔案的讀寫效率大大提高
              FileUtil.copyFile(new File("d:\\JAVAstudy\\JavaTest\\11.txt"), new File("d:\\JAVAstudy\\JavaTest\\111.txt"));
          } catch (IOException e) {
              e.printStackTrace();
          }
     }
}
class FileUtil{
     public static void copyFile(File src,File dst)throws IOException{
          FileInputStream fis=new FileInputStream(src);
          FileOutputStream fos=new FileOutputStream(dst);
          BufferedInputStream bis=new BufferedInputStream(fis);
          BufferedOutputStream bos=new BufferedOutputStream(fos);
          int data=;//用來儲存實際讀到的位元組數
          long time1=System.currentTimeMillis();
          //bis對象的read方法是直接從自己構造的緩沖區中讀取資料,緩沖資料是從fis填充過來的
          while(((data=bis.read())!=-)){
              //bos對象的write方法是從自己的緩沖區中一次性取資料後放入輸出流的
              bos.write(data);
          }
          bis.close();
          bos.close();
          long time2=System.currentTimeMillis();
          System.out.println("複制完成,共花費:"+(time2-time1)+"ms");
     }
}
           

5.2 DataInput/OutputStream

  • 資料輸入流允許應用程式以與機器無關方式從底層輸入流中讀取基本 Java 資料類型。應用程式可以使用資料輸出流寫入稍後由資料輸入流讀取的資料。
  • 資料輸出流允許應用程式以适當方式将基本 Java 資料類型寫入輸出流中。然後,應用程式可以使用資料輸入流将資料讀入。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataInputOutputStreamDemo {

     public static void main(String[] args)throws IOException {
          String name="the shanghai restoration project";
         int age=;
         boolean flag=true;
         char sex='男';
         double money=;

         //DataOutput
         DataOutputStream dos=new DataOutputStream(new FileOutputStream("d:\\JAVAstudy\\JavaTest\\b.txt"));
         dos.writeUTF(name);
         dos.writeInt(age);
         dos.writeBoolean(flag);
         dos.writeChar(sex);
         dos.writeDouble(money);
         dos.close();

         //DataInput
         DataInputStream dis=new DataInputStream(new FileInputStream("d:\\JAVAstudy\\JavaTest\\b.txt"));
         //讀的順序必須和寫的順序一緻,不然會産生亂碼
         System.out.println(dis.readUTF());
         System.out.println(dis.readInt());
         System.out.println(dis.readBoolean());
         System.out.println(dis.readChar());
         System.out.println(dis.readDouble());
         dis.close();
     }
}