java中把不同的輸入/輸出源(鍵盤、文件、網絡連接等)抽象表現為Stream(流).
java程序可以通過使用不同的流來訪問不同的輸入/輸出源.而Stream(流)可以直觀的理解為從數據的源(Source)到數據的接收(Sink)之間的這樣一段有序數據.
ps. 注意此處是Stream(流) 並不是 那款常喝的運動飲料的Scream(尖叫) 也不是冰激凌的奶油 Cream
大家看以參照下圖,在腦海中想象一下,途中的水滴就是流中的數據,他們共同組成了這段有序的數據(水:流)。
一、流的分類
1、按照流的方向(從程序運行所在的內存的角度來划分)
輸入流:只能從流中讀取數據,不能向其寫出數據.(基類 InputStream, Reader)
輸出流:只能向其寫出數據,不能從中讀入數據(基類OutputStream,Writer)
2、按照操作的數據單元不同
字節流:操作的最小數據單元是字節(8 bit),主要以InputStream、OutputSteam為基類
字符流:操作的最小數據單元是字符(16bit),主要以Reader、Writer為基類
3、按照流的角色分
節點流:可以從/向一個特定的IO設備讀寫數據的流。這種流屬於一種Low level Stream.
這種流往往直接連接到實際(防盜連接:本文首發自http://www.cnblogs.com/jilodream/ )的數據源,即和實際的輸入輸出節點直接連接.
處理流(包裝流):用於對已經存在的流進行連接和封裝,通過封裝后的流來實現數據的讀寫功能。這種流往往成為高級流。
這種流通過對不同節點流的包裝,使得程序可以使用完全相同的輸入輸出代碼來訪問不同的數據源。
是一種典型的外觀模式。即不改變原有類的使用和繼承的情況。通過創建一個包裝對象,來包裹真實的數據。此處原有類的使用和繼承就是節點流。通過包裝流來包裹真實數據,而擴展功能。
二、流的概念模型
流可以理解為:把輸入設備抽象成一個"水管",這個水管中的每個"水滴"依次排序,每一個水滴就可以理解為輸入輸出的單位(即前文中講到的字節和字符).這些水滴形成的一個有序序列就是流。
輸入流使用隱式的記錄指針來表示當前正准備從哪個水滴來開始讀取數據。每當程序從 inputStream/Reader 中取出一個或多個"水滴"的時候,記錄指針就會自動向后移動。
輸出流的話可以理解為在一根已經通向目標的管道邊,程序一次把輸出的"水滴"防止到需要輸出的管道中。
這樣我們就能理解流除了在內存中分配空間,還要占用操作系統的資源。
下面來看個例子
1 public class TempStreamModle 2 { 3 public static void main(String[] args) 4 { 5 PrintStream ps= null; 6 try 7 { 8 ps=new PrintStream(new FileOutputStream("out.txt"));// 創建輸出流 9 System.setOut(ps);//將輸出的流指定到ps流 10 System.out.println("common String"); 11 System.out.println(new TempStreamModle()); 12 } 13 catch(IOException ex) 14 { 15 //solve error 16 } 17 finally 18 { 19 if(ps!=null) 20 { 21 ps.close(); 22 } 23 } 24 } 25 }
Java的GC 只能回收jvm中不使用的對象,對操作系統分配的其他資源無法進行回收。所以需要我們主動的調用close()
調用close()方法的好處是:
(1)保證流占用的物理資源可以被釋放
(2)將輸出流緩沖中的數據flush(沖洗)到物理節點里。相當於主動執行了flush()方法。
PS. Java在使用流時,一般都會有一個緩沖區,按一種它認為比較高效的方法來發數據:應用程序每次IO都要和設備進行通信,效率很低,因此緩沖區為了提高效率,當寫入設備時,會先寫入緩沖區,等到緩沖區有足夠多的數據(防盜連接:本文首發自http://www.cnblogs.com/jilodream/ )時,就整體寫入設備(注意繪制GUI界面時,也是使用此種思路來保證界面在繪制或者拖動時不發生閃動)。而flush()表示強制將緩沖區中的數據發送出去,不必等到緩沖區滿再執行。所以如果在用流的時候,如果沒有用flush()這個方法,很多情況下會出現流的另外一側讀不到數據的問題,特別是在數據非常小的情況下。
這就相當於旅游區的景點觀光車一樣,人滿才會發車(flush),但是當下班時,無論最后一班車里有多少人,都會發最后一班車,而不會等到車滿(緩沖區滿)。