Java 輸入輸出流詳解

   最近一段時間,處於失業狀態,正好有機會對未理解的知識進行整理,面試中這部分的知識很大可能也是會遇到的。下面的內容一部分是從網上篩選的,經過自己的梳理後總結的。

 

Java IO 用的是‘decorator模式’來構建的。要容易理解Java IO 應該從對稱性和兩個模式(裝飾器模式和適配器模式)這兩方面進行分析。

 

裝飾器模式:

    在由InputStream、OutputStream、Reader和Writer 代表的等級結構內部,有一些流處理器可以對另外一些流處理器起到裝飾作用,形成新的、具有改善了的功能的流處理器。

 

下面是Decorator模式的結構圖:



1.Component就是裝飾器模式中公共方法的類,在裝飾器模式結構圖的頂層。

2.ConcreateComponent是轉換器模式中具體的被裝飾的類,IO包中的媒體流就是此種對象。

3.Decorator裝飾器模式中的核心對象,所有具體裝飾器對象的父類,完成裝飾器的部分職能。在上面的例子中Decorator類和這裏的對應。該類可以只做一些簡單的包裹被裝飾的對象,也可以還包含對Component中方法的實現……他有一個鮮明的特點:繼承至Component,同時包含一個Component作爲其成員變量。裝飾器模式動機中的動態地增加功能是在這裏實現的。

4.ConcreteDecoratorA和ConcreteDecoratorB是兩個具體的裝飾器對象,他們完成具體的裝飾功能。裝飾功能的實現是通過調用被裝飾對象對應的方法,加上裝飾對象自身的方法。這是裝飾器模式動機中的添加額外功能的關鍵。

 

裝飾器模式相關知識參考http://miaoxiaodong78.blog.163.com/blog/static/18765136200701232434996/

 

適配器模式:

  將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。 比如ByteArrayInputStream 是一個適配器類,它繼承了InputStream,而封裝了一個byte數組,換句話說,它將以個byte數組的接口適配成InputStream流處理器的接口。

適配器的結構圖:



 

適配器相關的知識參考:http://www.cnblogs.com/houleixx/archive/2008/03/04/1090214.html

 

下面開始瞭解Java IO

   Java IO 中的流從不同的角度可以分成三類:
1、按數據流的方向不同可以分爲輸入流(InputStream/Reader)和輸出流(OutputStream/Writer),用戶可以從輸入流中讀取信息,但不能寫它。相反,對輸出流,只能往往輸出流寫,而不能讀取。
2、按處理數據單位不同可以分爲字節流(InputStream/OutputStream-8位)和字符流(Reader/Writer-16位)。
3、按功能不同可以分爲節點流(原始流)和處理流(鏈接流)。節點流爲可以從一個特定的數據源(節點)讀寫數據(如文件、內存)。處理流是“連接”在已存在的流(節點流或處理流)之上,通過對數據的處理爲程序提供更爲強大的讀寫功能。

下面的分析是從功能上來分析的:

 

字節流處理類概述:

 

對於字節流處理的類都繼承自InputStream和OutputStream這兩個抽象類。

 

InputStream

 

InputStream結構圖,從圖中可以很清楚的看到裝飾器模式的應用。



 

InputStream提供的最重要的方法是:

read();
read(byte[] b) ;
read(byte[] b, int off, int len) ;

 

用於從輸入流中讀取字節。

 

原始流(也是一個適配器類):
(1)ByteArrayInputStream:爲多線程的通信提供緩衝區操作,接收一個Byte數據作爲流的源。


(2)FileInputStream:建立一個與文件有關的輸入流。接收一個File對象作爲流的源。


(3)PipedInputStream:可以與PipedOutputStream配合使用,用於讀入一個數據管道的數據,接收一個PipedOutputStream 作爲源。


(4)StringBufferInputStream:將一個字符串緩衝區轉換爲一個輸入流。接收一個String對象作爲流的源。


鏈接流:
(1)FilterInputStream稱爲過濾輸入流,它將另外一個輸入流作爲流源。這個類的子類包括一下幾種。
   BufferedInputStream: 爲另一個輸入流添加一些功能,即緩衝輸入以及支持 mark 和 reset 方法的能力。在創建BufferedInputStream 時,會創建一個內部緩衝區數組。在讀取或跳過流中的字節時,可根據需要從包含的輸入流再次填充該內部緩衝區,一次填充多個字節。mark 操作記錄輸入流中的某個點,reset 操作使得在從包含的輸入流中獲取新字節之前,再次讀取自最後一次 mark 操作後讀取的所有字節。
   DataInputStream:數據輸入流允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數據類型。應用程序可以使用數據輸出流寫入稍後由數據輸入流讀取的數據。
   LineNumberInputStream(已過時):提供帶有行計數功能的過濾輸入流。
   PushbackInputStream:提供特殊的功能,可以將已經讀取的字節“推回”到輸入流中。


(2)ObjectInputStream 對以前使用 ObjectOutputStream 寫入的基本數據和對象進行反序列化


(3)SequenceInputStream 表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的文件末尾爲止。

 

 

從結構圖中可以看出直接引用InputStream(相當於裝飾器模式中的Component) 的類就是鏈接流了。而FilterInputStream 相當於裝飾器模式中的Decorator。以下的也是一樣,所以就不一一分析了,其類的功能可查看API 文檔。


 OutputStream(和InputStream相對應)

OutputStream結構圖:

 

 

 

 

OutputStream提供的最重要的方法是:

write(int b);
write(byte[] b);
write(byte[] b, int off, int len) 

 用於將字節寫入輸出流。

 

 

 

字符流處理概述:

 

所有的字符流操作類都繼承自Reader或者Writer這兩個抽象類。

 

Reader

 

Reader結構圖:



 

Reader提供的重要方法有:

read(char[] cbuf);
read(char[] cbuf, int off, int len);
read(CharBuffer target);

他們提供了從流中讀取數據到字符數組或者CharBuffer的功能。

 

Writer

 

Writer結構圖:



 

 

Writer提供的重要方法有:

write(char[] cbuf);
write(char[] cbuf, int off, int len);
write(int c);
write(String str);
write(String str, int off, int len);

 

他們提供了把字符、字符數組或者字符串寫入流中的功能。

 

 

 

 字節流和字符流之間的轉換(從字節流適配到字符流)


InputStreamReader和OutputStreamReader:把一個以字節爲導向的stream轉換成一個以字符爲導向的stream。
InputStreamReader 是字節流通向字符流的橋樑:它使用指定的 charset 讀取字節並將其解碼爲字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集 。


OutputStreamWriter 是字符流通向字節流的橋樑:可使用指定的 charset 將要寫入流中的字符編碼成字節。它使用的字符集可以由名稱指定或顯式給定,否則將接受平臺默認的字符集。

 

RandomAccessFile

RandomAccessFile:此類的實例支持對隨機訪問文件的讀取和寫入。隨機訪問文件的行爲類似存儲在文件系統中的一個大型 byte 數組。存在指向該隱含數組的光標或索引,稱爲文件指針;輸入操作從文件指針開始讀取字節,並隨着對字節的讀取而前移此文件指針。如果隨機訪問文件以讀取/寫入模式創建,則輸出操作也可用;輸出操作從文件指針開始寫入字節,並隨着對字節的寫入而前移此文件指針。寫入隱含數組的當前末尾之後的輸出操作導致該數組擴展。該文件指針可以通過 getFilePointer 方法讀取,並通過 seek 方法設置。

 

I/O應用的例子

 

package JavaIO;

import java.io.*;

public class JavaIO{
	public static void main(String[] args) throws Exception {
		// 1b. 接收鍵盤的輸入
		/*
		 * 由於想以緩存字節讀取方式從標準IO(鍵盤)中讀取數據,所以要
		 * 先把標準IO(System.in)轉換成字符導向的stream,再進行BufferedReader封裝。
		 */
		BufferedReader stdin = new BufferedReader(new InputStreamReader(
				System.in));
		System.out.println("Enter a line:");
		System.out.println(stdin.readLine());

		// 2. 從一個String對象中讀取數據
		/*
		 * 要以字符的形式從一個String對象中讀取數據,所以要產生一個StringReader類型的stream。
		 */
		StringReader in2 = new StringReader("zhxing");
		int c;
		while ((c = in2.read()) != -1)
			System.out.println((char) c);
		in2.close();

		// 3. 從內存取出格式化輸入
		// 把內存中的一個緩衝區作爲DataInputStream使用
		try {
			DataInputStream in3 = new DataInputStream(new ByteArrayInputStream(
					"zhxing".getBytes()));
			while (true)
				System.out.println((char) in3.readByte());
		} catch (EOFException e) {
			System.out.println("End of stream");
		}

		// 4. 輸出到文件
		/*
		 * 對String對象s2讀取數據時,先把對象中的數據存入緩存中,再從緩衝中進行讀取;對TestIO.out文件進行操作時,
		 * 先把格式化後的信息輸出 到緩存中,再把緩存中的信息輸出到文件中。
		 */
		try {
			BufferedReader in4 = new BufferedReader(new StringReader("zhxing"));
			PrintWriter out1 = new PrintWriter(new BufferedWriter(
					new FileWriter("F:\\java\\ TestIO.out")));
			int lineCount = 1;
			String s = null;
			while ((s = in4.readLine()) != null)
				out1.println(lineCount++ + ":" + s);
			out1.close();
			in4.close();
		} catch (EOFException ex) {
			System.out.println("End of stream");
		}

		// 5. 數據的存儲和恢復
		/*
		 * 對Data.txt文件進行輸出時,是先把基本類型的數據輸出屋緩存中,再把緩存中的數據輸出到文件中;對文件進行讀取操作時,先把文件中的數據讀取到緩存中,再從緩存中以基本類型的形式進行讀取。注意in5.readDouble()這一行。因爲寫入第一個writeDouble(),所以爲了正確顯示。也要以基本類型的形式進行讀取。
		 */
		try {
			DataOutputStream out2 = new DataOutputStream(
					new BufferedOutputStream(new FileOutputStream(
							"F:\\java\\ Data.txt")));
			out2.writeDouble(3.1415926);
			out2.writeChars("\nThas was pi:writeChars\n");
			out2.writeBytes("Thas was pi:writeByte\n");
			out2.close();
			DataInputStream in5 = new DataInputStream(new BufferedInputStream(
					new FileInputStream("F:\\java\\ Data.txt")));
			BufferedReader in5br = new BufferedReader(
					new InputStreamReader(in5));
			System.out.println(in5.readDouble());
			System.out.println(in5br.readLine());
			System.out.println(in5br.readLine());
		} catch (EOFException e) {
			System.out.println("End of stream");
		}

		// 6. 通過RandomAccessFile操作文件
		// 通過RandomAccessFile類對文件進行操作。
		RandomAccessFile rf = new RandomAccessFile("F:\\java\\ rtest.dat", "rw");
		for (int i = 0; i < 10; i++)
			rf.writeDouble(i * 1.414);
		rf.close();
		rf = new RandomAccessFile("F:\\java\\ rtest.dat", "r");
		for (int i = 0; i < 10; i++)
			System.out.println("Value " + i + ":" + rf.readDouble());
		rf.close();
		rf = new RandomAccessFile("F:\\java\\ rtest.dat", "rw");
		rf.seek(5 * 8);
		rf.writeDouble(47.0001);
		rf.close();
		rf = new RandomAccessFile("F:\\java\\ rtest.dat", "r");
		for (int i = 0; i < 10; i++)
			System.out.println("Value " + i + ":" + rf.readDouble());
		rf.close();
	}

}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章