21 Java學習之位元組流(InputStream和OutPutStream)
一.流的分類
1、從功能上:輸入流、輸出流
2、從結構上:位元組流、字元流
3、從來源上:節點流、過濾流
其中InputStream/OutputStream是為位元組流而設計的,Reader/Writer是為字元流而設計的。處理位元組或者二進位制物件使用位元組流,處理字元或者字串使用字元流。
在最底層,所有的輸入/輸出都是位元組形式的,基於字元的流只在處理字元的時候提供方便有效的方法。
節點流是從特定的地方讀寫的流,例如磁碟或者記憶體空間,也就是這種流是直接對接目標的。
過濾流是使用節點流作為輸入輸出的,就是在節點流的基礎上進行包裝,新增一些特定的功能。
二. 什麼是輸入流和輸出流?
輸入和輸出流的概念其實都是針對記憶體(note:或者直接理解為我們的程式)說的。比如:我們常用來列印到控制檯的命令System.out.println()它就是out,對於記憶體來說,把字串列印到螢幕上就是從記憶體流向了螢幕的控制檯;而等待使用者輸入命令確實System.in就是從鍵盤將字元輸入到記憶體中。
因此: 從記憶體出:out (輸出流) 進入到記憶體:in(輸入流)
note:因為從記憶體到螢幕,就是寫檔案的流向;從硬碟到記憶體就是讀檔案的流向。
如果時網路訪問中,我們請求訪問網頁就是in,因為我們訪問頁面的時候時需要抓取該頁面的一個html檔案,因此是從網路到記憶體的流向;倘若有一個登陸頁面,那麼就是從記憶體到伺服器了,因為需要從記憶體寫資料到登陸介面,即out.
三. InputStream
其中有底色標註的為節點流,無底色標註的為過濾流,其中FilterInputStream在JDK中的定義為:包含其他一些輸入流,它將這些流用作其基本資料來源,可以直接傳輸資料或提供一些額外的功能,這個類本身並不經常被我們使用,常用的是它的子類。(note:上面InputStream的實現類沒有例完,只是常用的)
定義了位元組輸入模式的抽象類,該類提供了三個過載的read方法:
我們可以看到,三個read方法中,其中有一個是抽象的。那在這裡思考這樣一個問題:為什麼只有第一個是抽象的, 其他兩個是具體的?
因為後面兩個方法內部最終會去呼叫第一個方法,所以在InputStream派生類中只需要重寫第一個方法就可以了。在這裡可以看到第一個read方法是與具體的I/O裝置相關的,需要子類去實現。
1. 常用的位元組輸入流
- InputStream
- FileInputStream
- BufferedInputStream (BufferedInputStream不是InputStream的直接實現子類,是FilterInputStream的子類)
它們的區別與用途:
(1)InputStream:是抽象基類。其中定義了幾個特別常用的方法,比如read和close方法。
(2)FileInputStream:主要用來操作檔案的輸入流。
(3)BufferedInputStream:一般讀取是從硬盤裡面讀取資料;而帶有緩衝區之後,BufferedInputStream是提前將資料封裝到了記憶體中,因此記憶體中操作資料會更快,從而擁有比非緩衝區更高的效率。
2. 對寫資料的邏輯順序
(1)open a stream
(2)while more information
(3)read/wirte information
(4)close the stream
3. FileInputStream用法
1 package com.test.a; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 public class Test { 8 public static void main(String args[]) throws IOException { 9 InputStream is = new FileInputStream("C:\\Users\\hermioner\\Desktop\\test.txt"); 10 int length = 0; 11 byte[] buffer = new byte[20]; 12 StringBuffer stringBuffer = new StringBuffer(); 13 while (-1 != (length = is.read(buffer, 0, 20))) {//一箇中文字元佔兩個位元組 14 stringBuffer.append(new String(buffer, 0, length, "GBK"));// WINDOWS中用ANSI代表, 15 System.out.println(stringBuffer); 16 } 17 System.out.println(stringBuffer); 18 is.close(); 19 20 } 21 } 22 23 24 /** 25 * 1.建立一個檔案輸入流 is 26 * 2.建立一個位元組陣列,用它來存放每次讀取到記憶體中的內容,最多讀取20個位元組 27 * 3.必須要加入GBK,否則會亂碼。 28 * 4.讀完以後就關閉流is 29 * 30 * */View Code
1 輸出: 2 3 4 少年強則國強,國強少 5 少年強則國強,國強少年則更強 6 少年強則國強,國強少年則更強View Code
test.txt檔案中的內容是:
少年強則國強,國強少年則更強 (note:以ANSI格式儲存的)
4.BufferedInputStream用法
1 package com.test.a; 2 3 import java.io.BufferedInputStream; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 7 public class Test { 8 public static void main(String args[]) throws IOException { 9 FileInputStream fileInputStream=new FileInputStream("C:\\Users\\hermioner\\Desktop\\test.txt"); 10 BufferedInputStream bufferedInputStream=new BufferedInputStream(fileInputStream); 11 byte buffer[]=new byte[20]; 12 int len=0; 13 while((len=bufferedInputStream.read(buffer))!=-1) { 14 System.out.println(new String(buffer,0,len,"GBK")); 15 } 16 17 bufferedInputStream.close(); 18 fileInputStream.close(); 19 20 } 21 } 22 23 24 少年強則國強,國強少 25 年則更強View Code
5. 為什麼需要BufferedInputStream?
BufferedInputStream
和BufferedOutputStream
這兩個類分別是FilterInputStream
和FilterOutputStream
的子類,作為裝飾器子類,使用它們可以防止每次讀取/傳送資料時進行實際的寫操作,代表著使用緩衝區。
我們有必要知道不帶緩衝的操作,每讀一個位元組就要寫入一個位元組,由於涉及磁碟的IO操作相比記憶體的操作要慢很多,所以不帶緩衝的流效率很低。帶緩衝的流,可以一次讀很多位元組,但不向磁碟中寫入,只是先放到記憶體裡。等湊夠了緩衝區大小的時候一次性寫入磁碟,這種方式可以減少磁碟操作次數,速度就會提高很多!
同時正因為它們實現了緩衝功能,所以要注意在使用BufferedOutputStream
寫完資料後,要呼叫flush()
方法或close()
方法,強行將緩衝區中的資料寫出。否則可能無法寫出資料。與之相似還BufferedReader
和BufferedWriter
兩個類。
BufferedInputStream
和BufferedOutputStream
類就是實現了緩衝功能的輸入流/輸出流。使用帶緩衝的輸入輸出流,效率更高,速度更快。
四. OutPutStream
1. 常用的位元組輸出流
- OutputStream
- FileoutputStream
- BufferedOutputStream (BufferedOutputStream 不是OutputStream的直接實現子類,是FilterOutputStream的子類)
它們的區別與用途:
(1)OutputStream: 位元組輸出流的基類。在這個類中常用的方法有wirte、close和flush(即重新整理輸出流,把資料馬上寫到輸出流中)
(2)FileOutputStream:用於寫檔案的輸出流
(3)BufferedOutputStream:同BufferedInputStream,可以提高效率。
2. FileOutputStream的用法
1 package com.test.a; 2 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 6 public class Test { 7 public static void main(String args[]) throws IOException { 8 FileOutputStream fileOutputStream=new FileOutputStream("C:\\Users\\hermioner\\Desktop\\test.txt",true); 9 String string="為了中國夢,為了民族的偉大復興,為了老百姓的幸福"; 10 byte b[]=string.getBytes(); 11 fileOutputStream.write(b); 12 fileOutputStream.close(); 13 } 14 } 15 16 17 18 //note:上面的true代表在原來路徑下文字末尾追加欄位,如果不寫或者false則表示在檔案的開頭寫,即完成了覆蓋。如果上面給定的路徑不存在,則會新建立。View Code
3.BufferedOutputStream的用法
1 public class Test { 2 public static void main(String args[]) throws IOException { 3 FileOutputStream fileOutputStream=new FileOutputStream("C:\\Users\\hermioer\\Desktop\\test.txt"); 4 BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(fileOutputStream); 5 String string="為了中國夢,為了民族的偉大復興,為了老百姓的幸福"; 6 byte b[]=string.getBytes(); 7 bufferedOutputStream.write(b); 8 bufferedOutputStream.close();//BufferedOoutputStream中實際上沒有close,呼叫的是父類的close。並且close方法中還呼叫了flush方法 9 } 10 }View Code
參考文獻:
https://www.cnblogs.com/dongguacai/p/5658388.html
https://www.cnblogs.com/progor/p/9357676.html
https://blog.csdn.net/zhaoyanjun6/article/details/54894451