## Java編程那些事兒90——裝飾流使用1
陳躍峰
出自:[http://blog.csdn.net/mailbomb](http://blog.csdn.net/mailbomb)
### 11.3.3 裝飾流使用
除了按照流的方向可以把流劃分為輸入流和輸出流兩類,按照流讀寫數據的基本單位把流劃分為字節流和字符流兩類以外,還可以按照流是否直接連接實際數據源,例如文件、網絡、字節數組等,將流又可以劃分為實體流和裝飾流兩大類。
其中實體流指直接連接數據源的流類,如前面介紹的FileInputStream/FileOutputStream和FileReader和FileWriter,該類流直接實現將數據源轉換為流對象,在實體流類中實現了流和數據源之間的轉換,實體流類均可單獨進行使用。
而裝飾流指不直接連接數據源,而是以其它流對象(實體流對象或裝飾流對象)為基礎建立的流類,該類流實現了將實體流中的數據進行轉換,增強流對象的讀寫能力,比較常用的有DataInputStream/DataOutputStream和BufferedReader/BufferedWriter等,裝飾流類不可以單獨使用,必須配合實體流或裝飾流進行使用。
由于裝飾流都是在已有的流對象基礎上進行創建的,所以這種創建流的方式被稱作“流的嵌套”,通過流的嵌套,可以修飾流的功能,例如使讀寫的速度增加或者提供更多的讀寫方式,方便數據格式的處理。
裝飾流不改變原來實體流對象中的數據內容,只是從實體流對象基礎上創建出的裝飾流對象相對于實體流對象進行了一些功能的增強。
流的嵌套是學習IO編程時必須掌握的知識,使用它才可以讓你真正體會到IO類設計時的設計思路,也可以方便的使用IO類。
下面分別以DataInputStream/DataOutputStream和BufferedReader/BufferedWriter為例子,詳細介紹裝飾類的使用。
#### 11.3.3.1 DataInputStream/DataOutputStream
在前面的示例中,在向流中寫入的數據必須首先轉換為byte數組或char數組,當寫入的數據比較少、比較簡單時,則向流中寫入數據時還是不是很麻煩的,但是如果向流中寫入數據比較多時,手動轉換數據格式則會比較麻煩。當然,很多文件都是根據文件存儲的需要設計了專門的存儲格式,但是這些格式一般都比較復雜,需要閱讀專門的格式文檔才可以讀寫這些特定格式的文件。
為了簡化程序員對于流的操作,使得程序員可以從繁雜的數據格式中解脫出來,在IO類中專門設計了兩個類——DataInputStream/DataOutputStream類簡化流數據的讀寫,使用這兩個類,可以實現以增強型的讀寫方法讀寫數據,使得讀寫流的數據變得比較簡單。
在實際使用這兩個類時,必須匹配起來進行使用。也就是說,只有使用DataOutputStream流格式寫入的數據,在實際讀取時才可以使用DataInputStream進行讀取。因為在使用DataOutputStream向流中寫入數據時,除了寫入實際的數據內容以外,還寫入了特定的數據格式,該格式對于程序員來說是透明的,這種特定的格式不需要程序員熟悉,而只需要使用DataInputStream讀取即可,讀取時的順序和寫入時的順序和類型保持一致即可。
在DataInputStream類中,增加了一系列readXXX的方法,例如readInt、readUTF、readBoolean等等,而在DataOutputStream類中,也增加了一系列writeXXX的方法,例如writeInt、writeUTF、writeBoolean等等,使得對于數據的讀寫更加方便很容易。
下面以讀寫文件為例子,演示DataInputStream/DataOutputStream類的基本使用。
~~~
/**
?* 模擬需要存儲到文件中的數據
?* 該類中保存4種類型的數據
?*/
public class MyData {
???????? boolean b;
???????? int n;
???????? String s;
???????? short sh[];
????????
???????? public MyData(){}
????????
???????? public MyData(boolean b,int n,String s,short sh[]){
?????????????????? this.b = b;
?????????????????? this.n = n;
?????????????????? this.s = s;
?????????????????? this.sh = sh;
???????? }
}
~~~
在該示例中,需要將MyData類型的對象內部保存的數據按照一定的格式存儲到文件中,這里列舉了2種基本數據類型boolean和int,以及兩種引用數據類型String和數組,在下面的示例代碼中將會以一定的格式寫入到文件中。
~~~
import java.io.*;
/**
?* 使用DataOutputStream書寫具有一定格式的文件
?*/
public class WriteFileUseDataStream {
???????? public static void main(String[] args) {
?????????????????? short sh[] = {1,3,134,12};
?????????????????? MyData data =new MyData(true,100,"Java語言",sh);
?????????????????? //寫入文件
?????????????????? writeFile(data);
???????? }
????????
???????? /**
???????? ?* 將MyData對象按照一定格式寫入文件中
???????? ?* @param data 數據對象
???????? ?*/
???????? public static void writeFile(MyData data){
?????????????????? FileOutputStream fos = null;
?????????????????? DataOutputStream dos = null;
?????????????????? try{
??????????????????????????? //建立文件流
??????????????????????????? fos = new FileOutputStream("test.my");
??????????????????????????? //建立數據輸出流,流的嵌套
??????????????????????????? dos = new DataOutputStream(fos);
??????????????????????????? //依次寫入數據
??????????????????????????? dos.writeBoolean(data.b);
??????????????????????????? dos.writeInt(data.n);
??????????????????????????? dos.writeUTF(data.s);
??????????????????????????? //寫入數組
??????????????????????????? int len = data.sh.length;
??????????????????????????? dos.writeInt(len); //數組長度
??????????????????????????? //依次寫入每個數組元素
??????????????????????????? for(int i = 0;i < len;i++){
???????????????????????????????????? dos.writeShort(data.sh[i]);
??????????????????????????? }
?????????????????? }catch(Exception e){
??????????????????????????? e.printStackTrace();
?????????????????? }finally{
??????????????????????????? try {
???????? ??????????????????????????? dos.close();
???????????????????????????????????? fos.close();
??????????????????????????? } catch (Exception e2){
???????????????????????????????????? e2.printStackTrace();
??????????????????????????? }
?????????????????? }
???????? }
}
~~~
在該示例代碼中,首先建立一個實體流fos,該實體流連接到數據源——文件,然后以該實體流對象為基礎,使用流的嵌套,建立裝飾流對象dos,由于需要寫入流中的對象data中包含的數據比較多,所以需要以一定的格式寫入流,這里使用DataOutputStream避免自定義數據格式,而寫入流中的順序就是該流的格式,也就是文件test.my的格式,這種格式對于程序員來說是透明的。
使用對象dos中對應的writeXXX方法依次將需要存儲的數據寫入流中,在寫入字符串時,為了使字符編碼保持一致,一般使用writeUTF寫入字符串,也就是先將字符串轉換為utf-8格式的byte數組,然后再將該數組以一定的格式寫入到流中。而在寫入數組時,則首先寫入數組的長度,然后再將數組的內容依次寫入到流中,使用這種方式就可以很方便的將數組寫入到流中。
這樣文件test.my文件就具有了自己特定的文件格式,程序員需要記憶的就是該文件在寫入時的寫入順序,可以很方便的使用DataInputStream讀取出來。
下面的代碼是使用DataInputStream讀取test.my文件的代碼,注意文件格式的處理。
~~~
import java.io.*;
/**
?* 使用DataInputStream讀取自定義格式的文件
?*/
public class ReadFileUseDataStream {
???????? public static void main(String[] args) {
?????????????????? MyData data = readFile();
?????????????????? System.out.println(data.b);
?????????????????? System.out.println(data.n);
?????????????????? System.out.println(data.s);
?????????????????? int len = data.sh.length;
?????????????????? for(int i = 0;i < len;i++){
??????????????????????????? System.out.println(data.sh[i]);
?????????????????? }
???????? }
????????
???????? /**
???????? ?* 從文件test.my中讀取數據,并使用讀取到的數據初始化data對象
???????? ?* @return 讀取到的對象內容
???????? ?*/
???????? public static MyData readFile(){
?????????????????? MyData data = new MyData();
?????????????????? FileInputStream fis = null;
?????????????????? DataInputStream dis = null;
?????????????????? try {
??????????????????????????? //建立文件流
??????????????????????????? fis = new FileInputStream("test.my");
??????????????????????????? //建立數據輸入流,流的嵌套
??????????????????????????? dis = new DataInputStream(fis);
??????????????????????????? //依次讀取數據,并賦值給data對象
??????????????????????????? data.b = dis.readBoolean();
??????????????????????????? data.n = dis.readInt();
??????????????????????????? data.s = dis.readUTF();
??????????????????????????? int len = dis.readInt();
??????????????????????????? data.sh = new short[len];
??????????????????????????? for(int i = 0;i < len;i++){
???????????????????????????????????? data.sh[i] = dis.readShort();
??????????????????????????? }??????????????????????????
?????????????????? } catch (Exception e) {
??????????????????????????? e.printStackTrace();
?????????????????? }finally{
??????????????????????????? try {
???????????????????????????????????? dis.close();
???????????????????????????????????? fis.close();
??????????????????????????? } catch (Exception e) {
???????????????????????????????????? e.printStackTrace();
??????????????????????????? }
?????????????????? }
?????????????????? return data;
???????? }
}
~~~
在該示例代碼中,首先建立實體流fis,然后以該流對象為基礎建立dos裝飾流,然后按照寫入文件的順序,依次將流中的數據讀取出來,并將讀取到的數值賦值給data對象中對應的屬性,從而實現將數據從文件中恢復到實際的對象。
最后再次強調,DataInputStream和DataOutputStream必須匹配起來進行使用,也就是使用DataInputStream讀取的流數據必須是使用DataOutputStream流寫入的數據,這樣才能保持格式上的統一。
當然,使用DataInputStream和DataOutputStream和其它的實體流也可以匹配起來進行使用,例如和ByteArrayInputStream和ByteArrayOutputStream匹配使用將可以實現方便的把數據轉換為特定格式的byte數組以及將byte數組恢復回來,使用的格式和上面的示例類似,這里就不再重復了。
- 前言
- (1)序言
- (2)程序設計是什么?
- (3)你適合學習程序設計嗎?
- (4)如何學好程序設計?
- (5)程序設計介紹小結
- (6)計算機軟件基本概念
- (7)進制的概念
- (8)計算機內部的數據表達
- (9)網絡編程基礎
- (10)Java語言簡介
- (11)JDK的獲得、安裝和配置
- (12)第一個HelloWorld程序
- (13)Eclipse基本使用
- (14)Eclipse基礎使用進階
- (15)如何學好Java語法
- (16)代碼框架、關鍵字和標識符
- (17)基本數據類型
- (18)變量和常量
- (19)數據類型轉換
- (20)空白、語句結束和注釋
- (21)算術運算符
- (22)比較運算符
- (23)邏輯運算符
- (24)賦值運算符
- (25)位運算符
- (26)移位運算符
- (27)其它運算符
- (28)運算符優先級
- (29)表達式
- (30)流程控制基礎
- (31)if語句語法(1)
- (32)if語句語法(2)
- (33)if語句語法(3)
- (34)switch語句語法
- (35)while語句語法
- (36)do-while語句語法
- (37)for語句語法
- (38)break和continue語句
- (39)流程控制綜合示例1
- (40)流程控制綜合示例2
- (41)流程控制綜合示例3
- (42)流程控制綜合練習
- (43)數組概述
- (44)數組基礎語法
- (45)數組使用示例1
- (46)數組使用示例2
- (47)數組使用示例3
- (48)多維數組基礎
- (49)多維數組使用示例1
- (50)多維數組使用示例2
- (51)多維數組練習
- (52)方法聲明
- (53)方法聲明示例
- (54)方法調用
- (55)方法重載和參數傳遞
- (56)方法練習
- (57)面向對象基礎
- (58)類(一)
- (59)類(二)
- (60)對象
- (61)面向對象設計方法和面向對象特性(一)
- (62)繼承(二)
- (63)多態性
- (64)訪問控制符、修飾符和其它關鍵字
- (65)static修飾符
- (66)final修飾符
- (67)this和super
- (68)抽象類和接口(一)
- (69)抽象類和接口(二)
- (70)抽象類和接口(三)
- (71)內部類簡介
- (72)包的概念
- (73)JDK文檔使用
- (74)java.lang包介紹1
- (75)String類使用
- (76)StringBuffer類和System類
- (77)包裝類
- (78)時間和日期處理
- (79)Random隨機處理
- (80)集合框架簡述
- (81)異常處理概述
- (82)異常處理語法1
- (83)異常處理語法2
- (84)IO簡介
- (85)IO類體系
- (86)文件操作之File類使用
- (87)文件操作之讀取文件
- (88)文件操作之寫文件
- (89)讀取控制臺輸入
- (90)裝飾流使用1
- (91)裝飾流使用2
- (92)IO使用注意問題
- (93)多線程基礎
- (94)多線程實現方式1
- (95)多線程實現方式2
- (96)多線程使用示例1
- (97)多線程使用示例2
- (98)多線程問題及處理1
- (99)多線程問題及處理2
- (100)多線程問題及處理3
- (101)網絡編程概述
- (102)網絡編程技術1
- (103)網絡編程技術2
- (104)網絡編程技術3
- (105)網絡編程技術4
- (106)網絡編程技術5
- (107)網絡協議概念
- (108)網絡編程示例1
- (109)網絡編程示例2
- (110)網絡編程小結