字節流類爲處理字節式輸入/輸出提供了豐富的環境。
一個字節流可以和其他任何類型的對象並用,包括二進制數據。
這樣的多功能性使得字節流對很多類型的程序都很重要。
字節流類以InputStream 和OutputStream爲頂層。
一、InputStream(輸入流)
InputStream 是一個定義了Java 流式字節輸入模式的抽象類
該類的所有方法在出錯條件下引發一個IOException 異常
- //返回當前可讀的輸入字節數
- int available( )
- //關閉輸入源。關閉之後的讀取會產生IOException異常
- void close( )
- //如果下一個字節可讀則返回一個整型,遇到文件尾時返回-1
- int read()
- //試圖讀取buffer.length個字節到buffer中,並返回實際成功讀取的字節數。遇到文件尾時返回-1
- int read(byte buffer[ ])
- //重新設置輸入指針到先前設置的標誌處
- void reset( )
- //在輸入流的當前點放置一個標記。該流在讀取numBytes個字節前都保持有效
- void mark(int numBytes)
- //如果調用的流支持mark( )/reset( )就返回true
- boolean markSupported( )
- //忽略numBytes個輸入字節,返回實際忽略的字節數
- ong skip(long numBytes)
二、OutputStream(輸出流)
OutputStream是定義了流式字節輸出模式的抽象類。
該類的所有方法返回一個void 值 並且在出錯情況下引發一個IOException異常。
- //關閉輸出流。關閉後的寫操作會產生IOException異常
- void close( )
- //定製輸出狀態以使每個緩衝器都被清除,也就是刷新輸出緩衝區
- void flush( )
- //向輸出流寫入單個字節。注意參數是一個整型數,它允許你不必把參數轉換成字節型就可以調用write()
- void write(int b)
- //向一個輸出流寫一個完整的字節數組
- void write(byte buffer[ ])
- //寫數組buffer以buffer[offset]爲起點的numBytes個字節區域內的內容
- void write(byte buffer[ ], int offset, int numBytes)
三、FileInputStream(文件輸入流)
FileInputStream 類創建一個能從文件讀取字節的InputStream 類,它們都能引發FileNotFoundException 異常
- FileInputStream(String filepath)
- FileInputStream(File fileObj)
filepath 是文件的全稱路徑,fileObj是描述該文件的File對象。
下面的例子創建了兩個使用同樣磁盤文件且各含一個上述構造函數的FileInputStreams類:
- public static void main(String[] args) {
- try {
- FileInputStream stream=new FileInputStream("D:/test/txt");
- FileInputStream stream2=new FileInputStream(new File("D:/test/txt"));
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
↑↑↑儘管第一個構造函數可能更常用到,第二個構造函數允許在把文件賦給輸入流之前用File 方法更進一步檢查文件。
當一個FileInputStream 被創建時,它可以被公開讀取。FileInputStream重載了抽象類InputStream 的六個方法,mark( ) 和reset( ) 方法不被重載,任何關於使用FileInputStream的reset() 嘗試都會生成IOException異常。 |
↓↓↓下面的例題說明了怎樣讀取單個字節、字節數組以及字節數組的子界。它同樣闡述了怎樣運用available( ) 判定剩餘的字節個數及怎樣用skip( ) 方法跳過不必要的字節。該程序讀取它自己的源文件:
- public static void main(String[] args) {
- int size;
- try {
- InputStream f = new FileInputStream("D:/test.txt");
- System.out.println("\n當前可讀的輸入字節數:" + (size = f.available()));
- // 讀取單個字節
- System.out.print("讀取前文件中的前兩個字節:");
- for (int i = 0; i < 2; i++) {
- System.out.print((char) f.read());
- }
- System.out.println();
- System.out.println("\n當前可讀的輸入字節數:" + (size = f.available()));
- // 讀取字節數組
- byte b[] = new byte[10];
- if (f.read(b) != 10) {
- System.out.println("不能讀取10字節");
- }
- ;
- System.out.println(new String(b, 0, 10));
- System.out.println("\n當前可讀的輸入字節數:" + (size = f.available()));
- // 使用用skip()方法
- System.out.println("執行skip(2)跳躍兩個字節");
- f.skip(2);
- System.out.println("\n當前可讀的輸入字節數:" + (size = f.available()));
- if (f.read(b, 0, 5) != 5) {
- System.err.println("不能讀取5字節");
- }
- System.out.println(new String(b, 0, 5));
- System.out.println("\n當前可讀的輸入字節數:" + (size = f.available()));
- f.close();
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- // 輸出結果
- // ↓↓↓
- // 當前可讀的輸入字節數:490
- // 讀取前文件中的前兩個字節:In
- //
- // 當前可讀的輸入字節數:488
- // the Orien
- //
- // 當前可讀的輸入字節數:478
- // 執行skip(2)跳躍兩個字節
- //
- // 當前可讀的輸入字節數:476
- // young
- //
- // 當前可讀的輸入字節數:471
四、FileOutputStream(文件輸出流)
FileOutputStream 創建了一個可以向文件寫入字節的類OutputStream,它們可以引發IOException或SecurityException異常
- FileOutputStream(String filePath)
- FileOutputStream(File fileObj)
- FileOutputStream(String filePath, boolean append)
filePath是文件的全稱路徑,fileObj 是描述該文件的File 對象。如果append 爲true ,文件以設置搜索路徑模式打開。 |
FileOutputStream的創建不依賴於文件是否存在。在創建對象時FileOutputStream在打開輸出文件之前創建它。這種情況下你試圖打開一個只讀文件,會引發一個IOException異常。
- public static void main(String[] args) {
- try {
- String source = "Now is the time for all good men\n"
- + " to come to the aid of their country\n"
- + " and pay their due taxes.";
- byte buf[] = source.getBytes();
- OutputStream f0;
- f0 = new FileOutputStream("D:/file1.txt");
- for (int i = 0; i < buf.length; i += 2) {
- f0.write(buf[i]);
- }
- f0.close();
- OutputStream f1 = new FileOutputStream("D:/file2.txt");
- f1.write(buf);
- f1.close();
- OutputStream f2 = new FileOutputStream("D:/file3.txt");
- f2.write(buf, buf.length - buf.length / 4, buf.length / 4);
- f2.close();
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- // 輸出結果
- // ↓↓↓
- // file1.txt內容:
- // Nwi h iefralgo e
- // t oet h i ftercuty n a hi u ae.
- // file2.txt內容:
- // Now is the time for all good men
- // to come to the aid of their country
- // and pay their due taxes.
- // file3.txt內容:
- // nd pay their due taxes.
五、ByteArrayInputStream(字節數組輸入流)
ByteArrayInputStream是把字節數組當成源的輸入流。
- ByteArrayInputStream(byte array[])
- ByteArrayInputStream(byte array[], int start, int numBytes)
↑↑↑array是輸入源。第二個構造函數創建了一個InputStream 類,該類從字節數組的子集生成,以start 指定索引的字符爲起點,長度由numBytes決定。
- public static void main(String[] args) {
- String string = "abcdefghijklmnopqrstuvwxyz";
- byte b[] = string.getBytes();
- // 包含整個字母表中小寫字母
- ByteArrayInputStream stream = new ByteArrayInputStream(b);
- // 包含開始的十個字母。
- ByteArrayInputStream stream2 = new ByteArrayInputStream(b, 0, 10);
- }
ByteArrayInputStream實現mark( ) 和reset( ) 方法。然而,如果 mark( )不被調用,reset( )在流的開始設置流指針——該指針是傳遞給構造函數的字節數組的首地址。
下面的例子說明了怎樣用reset( ) 方法兩次讀取同樣的輸入:
- public static void main(String[] args) {
- String string = "abcdefg";
- byte b[] = string.getBytes();
- ByteArrayInputStream stream = new ByteArrayInputStream(b);
- for (int i = 0; i < 2; i++) {
- int c;
- while ((c = stream.read()) != -1) {
- System.out.print(i == 0 ? ((char) c) : Character
- .toUpperCase((char) c));
- }
- stream.reset();
- System.out.println();
- }
- }
- // 輸出結果
- // ↓↓↓
- // abcdefg
- // ABCDEFG
該例先從流中讀取每個字符,然後以小寫字母形式打印。然後重新設置流並從頭讀起,這次在打印之前先將字母轉換成大寫字母。
六、 ByteArrayOutputStream(字節數組輸出流)
ByteArrayOutputStream 是一個把字節數組當作輸出流的實現
- ByteArrayOutputStream( )
- ByteArrayOutputStream(int numBytes)
↑↑↑一個32 位字節的緩衝器被生成。第二個構造函數生成一個跟指定numBytes相同位數的緩衝器
緩衝器保存在ByteArrayOutputStream 的受保護的buf 成員裏。緩衝器的大小在需要的情況下會自動增加。緩衝器保存的字節數是由ByteArrayOutputStream 的受保護的count域保存的。 |
- public static void main(String[] args) {
- try {
- ByteArrayOutputStream f = new ByteArrayOutputStream();
- String s = "abcdefghijklmnopqrstuvwxyz";
- byte buf[] = s.getBytes();
- f.write(buf);
- System.out.println(f.toString());
- for (int i = 0; i < buf.length; i++) {
- System.out.print((char) buf[i]);
- }
- System.out.println();
- byte b2[] = f.toByteArray();
- // 寫入文件
- OutputStream f2 = new FileOutputStream("D:/test.txt");
- // writeTo( ) 這一便捷的方法將f 的內容寫入test.txt
- f.writeTo(f2);
- f2.close();
- f.reset();
- for (int i = 0; i < 3; i++) {
- f.write('X');
- }
- System.out.println(f.toString());
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- // 輸出結果
- // ↓↓↓
- // abcdefghijklmnopqrstuvwxyz
- // abcdefghijklmnopqrstuvwxyz
- // XXX
七、過濾字節流
過濾流(filtered stream )僅僅是底層透明地提供擴展功能的輸入流(輸出流)的包裝。這些流一般由普通類的方法(即過濾流的一個超類)訪問。典型的擴展是緩衝,字符轉換和原始數據轉換。這些過濾字節流是FilterInputStream 和FilterOutputStream 。
- FilterOutputStream(OutputStream os)
- FilterInputStream(InputStream is)
八、緩衝字節流
對於字節流,緩衝流(buffered stream),通過把內存緩衝器連到輸入/輸出流擴展一個過濾流類。該緩衝器允許Java 對多個字節同時進行輸入/輸出操作,提高了程序性能。因爲緩衝器可用,所以可以跳過、標記和重新設置流。
BufferedInputStream(緩衝輸入流)
緩衝輸入/輸出是一個非常普通的性能優化。Java 的BufferedInputStream 類允許把任何InputStream 類“包裝”成緩衝流並使它的性能提高。
- BufferedInputStream(InputStream inputStream)
- BufferedInputStream(InputStream inputStream, int bufSize)
↑↑↑第一種形式生成了一個默認緩衝長度的緩衝流。第二種形式緩衝器大小是由bufSize傳入的。
- 使用內存頁或磁盤塊等的若干倍的緩衝區大小可以給執行性能帶來很大的正面影響。但這是依賴於執行情況的。
- 最理想的緩衝長度一般與主機操作系統、可用內存空間及機器配置有關。合理利用緩衝不需要特別複雜的操作。
- 一般緩衝大小爲8192個字節,給輸入/輸出流設定一個更小的緩衝器通常是好的方法。
- 用這樣的方法,低級系統可以從磁盤或網絡讀取數據塊並在緩衝器中存儲結果。
- 因此,即使你在InputStream 外同時讀取字節數據時,也可以在超過99.9% 的時間裏獲得快速存儲操作。
在緩衝器中使用 mark( )是受限的。意思是說你只能給mark( )定義一個小於流緩衝大小的參數。 |
該例運用mark(32),該方法保存接下來所讀取的32個字節
- public static void main(String[] args) {
- try {
- String s = "This is a © copyright symbol "
- + "but this is © not.\n";
- byte buf[] = s.getBytes();
- ByteArrayInputStream in = new ByteArrayInputStream(buf);
- // 包裝成緩衝輸入流
- BufferedInputStream f = new BufferedInputStream(in);
- int c;
- boolean marked = false;
- while ((c = f.read()) != -1) {
- switch (c) {
- case '&':
- if (marked) {
- marked = false;
- f.mark(32);
- } else {
- marked = true;
- }
- break;
- case ';':
- if (marked) {
- marked = false;
- System.out.print("(c)");
- } else {
- System.out.print((char) c);
- }
- break;
- case ' ':
- if (marked) {
- marked = false;
- f.reset();
- System.out.print("&");
- } else {
- System.out.print((char) c);
- }
- break;
- default:
- System.out.print((char) c);
- break;
- }
- }
- } catch (Exception e) {
- // TODO: handle exception
- }
- }
- // 輸出結果
- // ↓↓↓
- // This is a copy(c) copyright symbol but this is copy
BufferedOutputStream (緩衝輸出流)
BufferedOutputStream與任何一個OutputStream相同,除了用一個另外的flush( ) 方法來保證數據緩衝器被寫入到實際的輸出設備。因爲BufferedOutputStream是通過減小系統寫數據的時間而提高性能的,可以調用flush( ) 方法生成緩衝器中待寫的數據。 不像緩衝輸入,緩衝輸出不提供額外的功能,Java 中輸出緩衝器是爲了提高性能的。
- //第一種形式創建了一個使用512字節緩衝器的緩衝流。
- //第二種形式,緩衝器的大小由bufSize參數傳入
- BufferedOutputStream(OutputStream outputStream)
- BufferedOutputStream(OutputStream outputStream, int bufSize)
PushbackInputStream (推回輸入流)
緩衝的一個新穎的用法是實現推回(pushback ),Pushback 用於輸入流允許字節被讀取然後返回(即“推回”)到流
- //創建了一個允許一個字節推回到輸入流的流對象
- PushbackInputStream(InputStream inputStream)
- //創建了一個具有numBytes長度緩衝區的推回緩衝流
- PushbackInputStream(InputStream inputStream, int numBytes)
除了具有與InputStream 相同的方法,PushbackInputStream 提供了unread( ) 方法
- //推回ch的低位字節,它將是隨後調用read( ) 方法所返回的下一個字節
- void unread(int ch)
- //返回buffer緩衝器中的字節
- void unread(byte buffer[ ])
- //推回buffer中從offset處開始的numChars個字節
- void unread(byte buffer, int offset, int numChars)
例子:
- public static void main(String[] args) {
- try {
- String s = "if (a == 4) a = 0;\n";
- System.out.println(s);
- byte buf[] = s.getBytes();
- ByteArrayInputStream in = new ByteArrayInputStream(buf);
- PushbackInputStream push = new PushbackInputStream(in);
- int c;
- while ((c = push.read()) != -1) {
- switch (c) {
- case '=':
- if ((c = push.read()) == '=') {
- System.out.print(".eq.");
- } else {
- System.out.print("<-");
- push.unread(c);
- }
- break;
- default:
- System.out.print((char) c);
- break;
- }
- }
- } catch (Exception e) {
- // TODO: handle exception
- }
- }
- // 輸出結果
- // ↓↓↓
- // if (a == 4) a = 0;
- //
- // if (a .eq. 4) a <- 0;
PushbackInputStream具有使InputStream生成的 mark( ) 或 reset( )方法失效的副作用。用markSupported( )來檢查你運用mark( )/reset( )的任何流類。 |
九、 SequenceInputStream (順序輸入流)
SequenceInputStream 類允許連接多個InputStream 流
- SequenceInputStream(InputStream first, InputStream second)
- SequenceInputStream(Enumeration streamEnum)
十、PrintStream (打印流)
PrintStream 具有本書開始以來我們在System 文件句柄使用過的System.out所有的格式化性能。
- PrintStream(OutputStream outputStream)
- PrintStream(OutputStream outputStream, boolean flushOnNewline)
↑↑↑當flushOnNewline 控制Java 每次刷新輸出流時,輸出一個換行符(\n )。如果flushOnNewline 爲true ,自動刷新。若爲false ,刷新不能自動進行。第一個構造函數不支持自動刷新。
十一、RandomAccessFile (隨機訪問文件類)
RandomAccessFile 包裝了一個隨機訪問的文件。它不是派生於InputStream 和OutputStream,而是實現定義了基本輸入/輸出方法的DataInput 和DataOutput接口。它同樣支持定位請求——也就是說,可以在文件內部放置文件指針。
- RandomAccessFile(File fileObj, String access) throws FileNotFoundException
- RandomAccessFile(String filename, String access) throws FileNotFoundException
↑↑↑第一種形式,fileObj 指定了作爲File 對象打開的文件的名稱。第二種形式,文件名是由filename 參數傳入的
兩種情況下,access 都決定允許訪問何種文件類型。如果是“r”,那麼文件可讀不可寫,如果是“rw”,文件以讀寫模式打開。
//用來設置文件內部文件指針的當前位置,newPos 指文件指針從文件開始以字節方式指定新位置。調用seek( ) 方法後,接下來的讀或寫操作將在文件的新位置發生。
- void seek(long newPos) throws IOException
- //該方法通過指定的len設置正在調用的文件的長度,該方法可以增長或縮短一個文件。如果文件被加長,增加的部分是未定義的
- void setLength(long len) throws IOException