## Java編程那些事兒92——IO使用注意問題
陳躍峰
出自:[http://blog.csdn.net/mailbomb](http://blog.csdn.net/mailbomb)
### 11.3.4 注意問題
上面介紹了IO類的基本使用,熟悉了實體流和裝飾流的基本使用,但是在IO類實際使用時,還是會遇到一系列的問題,下面介紹一些可能會經常遇到的問題。
#### 11.3.4.1 類的選擇
對于初次接觸IO技術的初學者來說,IO類體系博大精深,類的數量比較龐大,在實際使用時經常會無所適從,不知道該使用那些類進行編程,下面介紹一下關于IO類選擇的一些技巧。
選擇類的第一步是選擇合適的實體流。
選擇實體流時第一步是按照連接的數據源種類進行選擇,例如讀寫文件應該使用文件流,如FileInputStream/FileOutputStream、FileReader/FileWriter,讀寫字節數組應該使用字節數組流等,如ByteArrayInputStream/ByteArrayOutputStream。
選擇實體流時第二步是選擇合適方向的流。例如進行讀操作時應該使用輸入流,進行寫操作時應該使用輸出流。
選擇實體流時第三步是選擇字節流或字符流。除了讀寫二進制文件,或字節流中沒有對應的流時,一般都優先選擇字符流。
經過以上步驟以后,就可以選擇到合適的實體流了。下面說一下裝飾流的選擇問題。
在選擇IO類時,實體流是必需的,裝飾流是可選的。另外在選擇流時實體流只能選擇一個,而裝飾流可以選擇多個。
選擇裝飾流時第一步是選擇符合要求功能的流。例如需要緩沖流的話選擇BufferedReader/BufferedWriter等,有些時候也可能只是為了使用某個裝飾流內部提供的方法。
選擇裝飾流時第二步是選擇合適方向的流,這個和實體流選擇中的第二步一致。
當選擇了多個裝飾流以后,可以使用流之間的多層嵌套實現要求的功能,流的嵌套之間沒有順序。
#### 11.3.4.2? 非依次讀取流數據
由于IO類設計的特點,在實際讀取時,只能依次讀取流中的數據,而且在通常情況下,已經讀取過的數據無法再進行讀取。如果需要重復讀取流中某段數據時,一般的做法是將從流中讀取的數據使用數組存儲起來,然后根據需要讀取數組中的內容即可,但是有些時候,還是有一些特殊的情況的,IO類對于這些都進行了支持。
1、間斷性的讀取流中的數據
對于某些特殊格式的文件,例如字體文件等,在實際讀取數據時不需要順序進行讀取,而只需要根據內容的位置進行讀取。這樣可以使用流中的skip方法實現。例如:
int n = fis.skip(100);
該行代碼的作用是,以流fis當前位置為基礎,當前位置可以是流中的任何位置,向后跳過100個單位(字節流單位為字節,字符流單位是字符),如果再使用read方法繼續讀取,就是讀取跳躍以后新位置的內容,也就相當于跳過了100個單位的內容。
而實際在使用時,實際真正跳過的單位數量作為skip方法的返回值返回。
2、重復讀取流中某段數據
當必須重復讀取流中同一段數據時,如果對應的流支持mark(標記)的話,則可以重復讀取同一段數據。
下面以重復讀取控制臺輸入流System.in為例子,來介紹mark的使用,示例代碼如下:
~~~
import java.io.*;
/**
?* mark使用示例
?*/
public class MarkUseDemo {
???????? public static void main(String[] args) {
?????????????????? byte[] b = new byte[1024];
?????????????????? try{
??????????????????????????? //讀取數據
??????????????????????????? int data = System.in.read();
??????????????????????????? //輸出第一個字節的數據
??????????????????????????? System.out.println("第一個字節:" + data);
??????????????????????????? //判斷該流是否支持mark
??????????????????????????? if(System.in.markSupported()){
???????????????????????????????????? //記憶當前位置,可以從當前位置
???????????????????????????????????? //向后最多讀取100個字節
???????????????????????????????????? System.in.mark(100);
???????????????????????????????????? //讀取數據
???????????????????????????????????? int n = System.in.read(b);
???????????????????????????????????? //輸出讀取到的內容
???????????????????????????????????? System.out.print("第一次讀取到的內容:");
???????????????????????????????????? for(int i = 0;i < n;i++){
?????????????????????????????????????????????? System.out.print(b[i] + " ");
???????????????????????????????????? }
???????????????????????????????????? System.out.println();
???????????????????????????????????? //回到標記位置
???????????????????????????????????? System.in.reset();
???????????????????????????????????? //重復讀取標記位置以后的內容
???????????????????????????????????? n = System.in.read(b);
???????????????????????????????????? //輸出讀取到的內容
???????????????????????????????????? System.out.print("第二次讀取到的內容:");
???????????????????????????????????? for(int i = 0;i < n;i++){
?????????????????????????????????????????????? System.out.print(b[i] + " ");
???????????????????????????????????? }
???????????????????????????????????? System.out.println();
??????????????????????????? }
?????????????????? }catch(Exception e){
??????????????????????????? e.printStackTrace();
?????????????????? }
???????? }
}
~~~
在該示例中,首先調用System.in流中的read方法,讀取流中的第一個字節,并把讀取到的數據賦值給data,然后將讀取到的第一個字節的數據輸出出來。
然后調用System.in流中的markSupported判斷該流是否支持mark功能,如果支持的話則markSupported方法將返回true。
如果流System.in支持mark,則標記當前位置,并允許從當前位置開始最多讀取后續100個字節的數據,其實IO類內部的只讀取這些數據,而不真正從流中將這些數據刪除。
后續繼續讀取流中的數據,如果讀取的數據超過100個字節,則mark標記失效,并把讀取到的有效數據輸出到控制臺。
如果需要從標記位置重復讀取已經讀取過的數據,則只需要調用流對象中的reset方法重置流的位置,使流可以回到mark的位置,如果繼續讀取的話,則從該位置開始可以向后讀取。這樣就可以從mark的位置開始,再次讀取后續的數據了。
例如在控制臺輸入123456789,則該程序的執行結果是:
第一個字節:49
第一次讀取到的內容:50 51 52 53 54 55 56 57 13 10
第二次讀取到的內容:50 51 52 53 54 55 56 57 13 10
其中輸入的第一個字節是1,讀取時該字符的編碼是49,而后續的內容就是流的結構,其中流末尾的13和10是在輸入時,添加的回車和換行字符。
#### 11.3.4.3 中文問題
由于JDK設計時,對于國際化支持比較好,所以JDK在實際實現時支持很多的字符集,這樣在進行特定字符集的處理時就需要特別小心了。
其實在進行中文處理時,只需要注意一個原則就可以了,這個原則就是將中文字符轉換為byte數組時使用的字符集,需要和把byte數組轉換為中文字符串時的字符集保持一致,這樣就不會出現中文問題了。
當然,如果不想手動實現字符串和byte數組的轉換,可以使用DataInputStream和DataOutputStream中的readUTF和writeUTF實現讀寫字符串。
### 11.4 總結
關于IO類的使用,還需要在實際開發過程中多進行使用,從而更深入的體會IO類設計的初衷,并掌握IO類的使用。
另外,IO類是Java中進行網絡編程的基礎,所以熟悉IO類的使用也是學習網絡編程必須的一個基礎。
- 前言
- (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)網絡編程小結