原文出處:http://blog.csdn.net/gnuhpc/archive/2009/10/22/4713381.aspx
1.基本概念和基本情況:
a.流:一組有序的數據序列。
b.字節流:數據流中最小的數據單元是字節。
c.字符流:數據流中最小的數據單元是字符。
d.java.io.InputStream和java.io.OutputStream分別表示字節輸入流和字節輸出流。都是抽象類,不能被實例化。所有的字節操作流都是這兩個類的直接或間接子類。read()讀取的是一個8位字節,write()寫入的是一個8位字節。
e.java.io.Reader和java.io.Writer分別表示字符輸入流和字符輸出流。都是抽象類,不能被實例化。所有的字符操作流都是這兩個類的直接或間接子類。
2.字節輸入流:
1)字節數組輸入流:ByteArrayInputStream類
從內存中的字節數組中讀取數據,其數據源是一個字節數組。該類本身採用了適配器設計模式,將字節數組類型轉化爲輸入流,完成對數組的讀操作,讀到的每一個字節類型的元素都會自動轉換爲int。
使用如下:
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class Test {
public static void main(String[] args){
byte buff[]= new byte[]{2,15,67,-1,-9,9};
ByteArrayInputStream inputStream = new ByteArrayInputStream(buff,1,4);
int data = inputStream.read();
while (data!=-1) {
System.out.println(data + " ");
data = inputStream.read();
}
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
這裏的值會在前邊補0而導致變化,但是請注意在byte類型賦值給int類型時,取值不會變。
2)文件輸入流:FileInputStream類
從文件中讀取數據流。注意系統的默認編碼格式。
3)字符串輸入流:StringBufferInputStream類。(已被StringReader類代替而廢棄)
從一個字符串中讀取。本身採用了適配器設計模式。
4)管道輸入流PipedInputStream類
通常用於線程間通信,A執行管道輸入流的read方法,暫時沒有數據的時候這個線程就會被阻塞,只有當線程B向管道輸入流寫了新的數據後A纔會執行。
5)順序輸入流:SequenceInputStream類
用於將幾個輸入流串聯在一起,合併爲一個輸入流。當類通過這個類來讀取數據時,它會依次(以輸入流加入爲序) 從所有被串聯的輸入流中讀取數據。關閉輸入流不需要先依次關閉各個子輸入流。
6)過濾輸入流:FilterInputStream類
這是裝飾器,它本身繼承了InputStream類,還可以用來修飾其他的輸入流類。其構造方法爲protected訪問級別,因此外部程序不能創建這個類本身的實例。
關於裝飾器模式,在這摘錄一個網友(江蘇 無錫 繆小東 )的文字,我把一些調侃的話刪掉了,留其本質內容:
“三明治”的例子:三明治必不可少的是兩塊麪包片,然後可以在夾層里加上蔬菜、沙拉、鹹肉等等,外面可以塗上奶油之類的。假如現在你要爲一個三明治小店構造一個程序,其中要設計各種三明治的對象。可能你已經創建了一個簡單的Sandwich對象,現在要產生帶蔬菜的就是繼承原有的Sandwich添加一個蔬菜的成員變量,看起來很“正點”的做法,以後我還要帶鹹肉的、帶奶油的、帶蔬菜的又分爲帶青菜的、帶芹菜的、生菜的……還是一個一個繼承是吧!假如我們還需要即帶蔬菜又帶其它肉類,設置我們還要求這些添加成分的任意組合,那你就慢慢繼承吧!下面我們就使用裝飾器模式來設計這個庫吧!下圖是我們的設計圖:
下面是以上各個類的意義:
1. Ingredient(成分):所有類的父類,包括它們共有的方法,一般爲抽象類且方法都有默認的實現,也可以爲接口。它有Bread和Decorator兩個子類。這種實際不存在的,系統需要的抽象類僅僅表示一個概念,圖中用紅色表示。
2. Bread(麪包):就是我們三明治中必須的兩片面包。它是系統中最基本的元素,也是被裝飾的元素,和IO中的媒質流(原始流)一個意義。在裝飾器模式中屬於一類角色,所以其顏色爲紫色。
3. Decorator(裝飾器):所有其它成分的父類,這些成分可以是豬肉、羊肉、青菜、芹菜。這也是一個實際不存在的類,僅僅表示一個概念,即具有裝飾功能的所有對象的父類。圖中用藍色表示。
4. Pork(豬肉):具體的一個成分,不過它作爲裝飾成分和麪包搭配。
5. Mutton(羊肉):同上。
6. Celery(芹菜):同上。
7. Greengrocery(青菜):同上。
總結一下裝飾器模式中的四種角色:1.被裝飾對象(Bread);2.裝飾對象(四種);3.裝飾器(Decorator);4.公共接口或抽象類(Ingredient)。其中1和2是系統或者實際存在的,3和4是實現裝飾功能需要的抽象類。
寫段代碼體會其威力吧!(程序很簡單,但是實現的方法中可以假如如何你需要的方法,意境慢慢體會吧!)
//Ingredient.java
public abstract class Ingredient {
public abstract String getDescription();
public abstract double getCost();
public void printDescription(){
System.out.println(" Name "+ this.getDescription());
System.out.println(" Price RMB "+ this.getCost());
}
}
所有成分的父類,抽象類有一個描述自己的方法和一個得到價格的方法,以及一個打印自身描述和價格的方法(該方法與上面兩個方法構成模板方法哦!)
//Bread.java
public class Bread extends Ingredient {
private String description ;
public Bread(String desc){
this.description=desc ;
}
public String getDescription(){
return description ;
}
public double getCost(){
return 2.48 ;
}
}
麪包類,因爲它是一個具體的成分,因此實現父類的所有的抽象方法。描述可以通過構造器傳入,也可以通過set方法傳入。同樣價格也是一樣的,我就很簡單地返回了。
//Decorator.java
public abstract class Decorator extends Ingredient {
Ingredient ingredient ;
public Decorator(Ingredient igd){
this.ingredient = igd;
}
public abstract String getDescription();
public abstract double getCost();
}
裝飾器對象,所有具體裝飾器對象父類。它最經典的特徵就是:1.必須有一個它自己的父類爲自己的成員變量;2.必須繼承公共父類。 這是因爲裝飾器也是一種成分,只不過是那些具體具有裝飾功能的成分的公共抽象罷了。在我們的例子中就是有一個Ingredient作爲其成員變量。Decorator繼承了Ingredient類。
//Pork.java
public class Pork extends Decorator{
public Pork(Ingredient igd){
super(igd);
}
public String getDescription(){
String base = ingredient.getDescription();
return base +"/n"+"Decrocated with Pork !";
}
public double getCost(){
double basePrice = ingredient.getCost();
double porkPrice = 1.8;
return basePrice + porkPrice ;
}
}
具體的豬肉成分,同時也是一個具體的裝飾器,因此它繼承了Decorator類 。豬肉裝飾器裝飾可以所有的其他對象,因此通過構造器傳入一個Ingredient的實例,程序中調用了父類的構造方法,主要父類實現了這樣的邏輯關係。同樣因爲方法是具體的成分,所以getDescription得到了實現,不過由於它是具有裝飾功能的成分,因此它的描述包含了被裝飾成分的描述和自身的描述。價格也是一樣的。價格放回的格式被裝飾成分與豬肉成分的種價格哦!
從上面兩個方法中我們可以看出,豬肉裝飾器的功能得到了增強,它不僅僅有自己的描述和價格,還包含被裝飾成分的描述和價格。主要是因爲被裝飾成分是它的成員變量,因此可以任意調用它們的方法,同時可以增加自己的額外的共同,這樣就增強了原來成分的功能。
//Mutton.java
public class Mutton extends Decorator{
public Mutton(Ingredient igd){
super(igd);
}
public String getDescription(){
String base = ingredient.getDescription();
return base +"/n"+"Decrocated with Mutton !";
}
public double getCost(){
double basePrice = ingredient.getCost();
double muttonPrice = 2.3;
return basePrice + muttonPrice ;
}
}
羊肉的包裝器。
//Celery.java
public class Celery extends Decorator{
public Celery(Ingredient igd){
super(igd);
}
public String getDescription(){
String base = ingredient.getDescription();
return base +"/n"+"Decrocated with Celery !";
}
public double getCost(){
double basePrice = ingredient.getCost();
double celeryPrice =0.6;
return basePrice + celeryPrice ;
}
}
芹菜的包裝器。
//GreenGrocery.java
public class GreenGrocery extends Decorator{
public GreenGrocery (Ingredient igd){
super(igd);
}
public String getDescription(){
String base = ingredient.getDescription();
return base +"/n"+"Decrocated with GreenGrocery !";
}
public double getCost(){
double basePrice = ingredient.getCost();
double greenGroceryPrice = 0.4;
return basePrice + greenGroceryPrice ;
}
}
青菜的包裝器。
下面我們就領略裝飾器模式的神奇了!我們有一個測試類,其中建立夾羊肉的三明治、全蔬菜的三明治、全葷的三明治。
public class DecoratorTest{public static void main(String[] args){
Ingredient compound = new Mutton(new Celery(new Bread("Master24's Bread")));
compound.printDescription();
compound = new Celery(new GreenGrocery(new Bread("Bread with milk")));
compound.printDescription();
compound = new Mutton(new Pork(new Bread("Bread with cheese")));
compound.printDescription();
}
}
以上就是一個簡單的裝飾器類。
a.DataInputStream類
實現了DataInput接口,用於讀取基本類型數據。如int,long,float,double,boolean。其中的readUTF()方法還能讀取UTF-8編碼的字符串。其應該與DataOutputStream配套使用。也就是說,用DataInputStream讀取由DataOutputStream寫出的數據,才能保證獲得正確的數據。
b.LineNumberInputStream類:已被廢棄
c.BufferedInputStream類
覆蓋了被裝飾的輸入流的讀數據行爲,利用buffer來提供效率。一般在讀取數據文件或者鍵盤輸入時都使用其進行裝飾。
d.PushbackInputStream類
很少用到。
3.字節輸出流
1)字節數組輸出流:ByteArrayOutputStream類
向內存中的字節數組寫數據,使用toByteArray方法獲得字節數組。
2)文件輸出流:FileOutputStream類
向文件中寫數據。注意默認下使用則是覆蓋文件,必須使用FileOutputStream(String name,boolean append),在後邊設置爲true則是在追加。
3)過濾輸出流:FilterOutputStream類
和過濾輸入流的特性一樣。
a)DataOutputStream類:
用於向輸出流寫基本數據類型。
b)BufferedOutputStream類:
緩衝區提高效率。在超過緩衝區時會將緩衝區的內的數據寫入輸出流,若沒有超過則不會自動寫入,否則使用flush()方法刷新,或者關閉輸出流。
c)PrintStream類:
採用本地OS默認的字符編碼輸出格式化數據。每一個Print()方法都與一個println()方法對應。使用checkError()方法判斷寫數據是否成功。它也帶有緩衝區,不過用戶可以自己設置刷新規則(見其構造函數的一個重載)。
4.字符輸入流
注意BufferedReader不是FilterReader的子類。
1)字符數組輸入流:CharArrayReader類
從內存中的字符數組中讀取字符。
2)字符串輸入流:StringReader類
數據源是字符串。
3)InputStreamReader類:
主要是解決Unicode字符編碼和用戶指定編碼的變換問題。爲了提高效率則可以使用BufferedReader來修飾。它有個子類是FileReader類,但是其只能按照本地平臺的字符編碼來讀取數據,用戶不能指定編碼類型。
4)BufferedReader類:
帶有緩衝區的字符輸入流。
5.字符輸出流
1)字符數組輸出流:CharArrayWriter類
將內存中的字符中寫入字符數組。
2)BufferedWriter類:帶有緩衝。
3)OutputStreamWriter類:
爲了提高效率則可以使用BufferedWriter來修飾。它有個子類是FileWriter類,但是其只能按照本地平臺的字符編碼來寫入數據,用戶不能指定編碼類型。
4)PrintWriter類:能輸出格式化的數據。在輸出字符數據的場合,應該優先考慮用PrintWriter。
6.標準IO
三個標準輸入輸出流:
System.in:InputStream類,代表標準輸入流,默認爲鍵盤。常用套路是InputStreamReader適配器把System.in轉換爲Reader類型,再用BufferedReader裝飾它。
System.out:PrintStream類,代表標準輸出流,默認爲控制檯。通常轉化爲PrintWriter類型。
System.err:PrintStream類,代表標準錯誤輸出流,默認爲控制檯。
重定向使用System類中的,setIN(),setOut(),setErr()方法。
7.隨機訪問文件類:RandomAccessFile
可以從任意位置對文件進行讀寫。實現了DataInput和DataOutput接口,可以讀寫格式化的數據。
8.新IO庫的部分介紹
java.nio包中,目的在於提高IO操作效率。緩衝器Buffer類:減少了實際物理讀寫次數,減少動態分配和回收內存區域的次數。這個類可以使得程序直接控制和運用緩衝區。抽象類,有8個具體的緩衝區類,都有能返回自身實例的靜態工廠方法。
緩衝區的幾個屬性(也提供了改變這三個屬性的方法):
容量:緩衝區的大小。
極限:緩衝區的當前終點。這個點即爲實際的數據區的終點。
位置:緩衝區的下一個讀寫單元。
Channel是用來連接緩衝區與數據源的。通道在創建時被打開,一旦關閉就不能再打開了。
有兩個常用方法:一個是flip(),確保後續操作只針對在緩衝區中的當前數據,而clear()方法則把緩衝區的極限設爲容量值,爲後續向緩衝區填入更多的數據做準備。
2)字符編碼Charset類
其中的每個實例都代表特定的字符編碼類型。
3)FileChannel讀寫文件
放一個例子吧,從書上摘的:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;public class Test{
public static void main(String[] args) throws IOException{
final int SIZE = 1024;
/*向文件中寫數據*/
FileChannel fcChannel = new FileOutputStream("c://test.txt").getChannel();
fcChannel.write(ByteBuffer.wrap("你好,".getBytes()));//靜態方法wrap爲包裝爲一個ByteBuffer對象
fcChannel.close();
/*向文件末尾添加數據*/
fcChannel = new RandomAccessFile("c://test.txt","rw").getChannel();
fcChannel.position(fcChannel.size());//定位到文件尾
fcChannel.write(ByteBuffer.wrap("世界!".getBytes()));
fcChannel.close();
/*讀數據*/
fcChannel = new FileInputStream("c://test.txt").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(SIZE);//創建ByteBuffer對象
fcChannel.read(buffer);//將文件中的數據讀入緩衝區中
buffer.flip();//將極限設爲當前位置值,再把position設爲0,這使得接下來的cs.decode(buff)方法僅僅操作剛剛寫入緩衝區的數據。
Charset cs = Charset.defaultCharset();//獲得本地OS的字符編碼
System.out.println(cs.decode(buffer));//轉換爲Unicode字符編碼
fcChannel.close();
}
}