流的概念
流是一種抽象概念,它代表了數(shù)據(jù)的無結(jié)構(gòu)化傳遞措近。按照流的方式進行輸入輸出,數(shù)據(jù)被當(dāng)成無結(jié)構(gòu)的字節(jié)序或字符序列女淑。從流中取得數(shù)據(jù)的操作稱為提取操作瞭郑,而向流中添加數(shù)據(jù)的操作稱為插入操作。用來進行輸入輸出操作的流就稱為IO流鸭你。換句話說屈张,IO流就是以流的方式進行輸入輸出擒权。
IO流的分類
- 根據(jù)處理數(shù)據(jù)類型的不同分為:字符流和字節(jié)流
- 根據(jù)數(shù)據(jù)流向不同分為:輸入流和輸出流
字符流和字節(jié)流
字符流的由來: 因為數(shù)據(jù)編碼的不同,而有了對字符進行高效操作的流對象阁谆。本質(zhì)其實就是基于字節(jié)流讀取時碳抄,去查了指定的碼表。 字節(jié)流和字符流的區(qū)別:
? 讀寫單位不同:字節(jié)流以字節(jié)(8bit)為單位场绿,字符流以字符為單位剖效,根據(jù)碼表映射字符,一次可能讀多個字節(jié)焰盗。
? 處理對象不同:字節(jié)流能處理所有類型的數(shù)據(jù)(如圖片璧尸、avi等),而字符流只能處理字符類型的數(shù)據(jù)
結(jié)論:只要是處理純文本數(shù)據(jù)熬拒,就優(yōu)先考慮使用字符流爷光。 除此之外都使用字節(jié)流。
輸入流和輸出流
對輸入流只能進行讀操作澎粟,對輸出流只能進行寫操作蛀序,程序中需要根據(jù)待傳輸數(shù)據(jù)的不同特性而使用不同的流。
IO流的家族關(guān)系圖:
Java IO流對象
1.輸入字節(jié)流InputStream
1.1 InputStream 是所有的輸入字節(jié)流的父類活烙,它是一個抽象類徐裸。
1.2 ByteArrayInputStream、StringBufferInputStream瓣颅、FileInputStream 是三種基本的介質(zhì)流倦逐,它們分別從Byte 數(shù)組、StringBuffer宫补、和本地文件中讀取數(shù)據(jù)檬姥。PipedInputStream 是從與其它線程共用的管道中讀取數(shù)據(jù),與Piped 相關(guān)的知識后續(xù)單獨介紹粉怕。
1.3 ObjectInputStream 和所有FilterInputStream 的子類都是裝飾流
(裝飾器模式的主角)健民。
2.輸出字節(jié)流OutputStream
- OutputStream 是所有的輸出字節(jié)流的父類,它是一個抽象類贫贝。
- ByteArrayOutputStream秉犹、FileOutputStream 是兩種基本的介質(zhì)流,它們分別向Byte 數(shù)組稚晚、和本地文件中寫入數(shù)據(jù)崇堵。PipedOutputStream 是向與其它線程共用的管道中寫入數(shù)據(jù)。
- ObjectOutputStream 和所有FilterOutputStream 的子類都是裝飾流客燕。
3.字節(jié)流的輸入與輸出的對應(yīng)
圖中藍色的為主要的對應(yīng)部分鸳劳,紅色的部分就是不對應(yīng)部分。紫色的虛線部分代表這些流一般要搭配使用也搓。從上面的圖中可以看出Java IO 中的字節(jié)流是極其對稱的赏廓。
“存在及合理”我們看看這些字節(jié)流中不太對稱的幾個類:
- LineNumberInputStream 主要完成從流中讀取數(shù)據(jù)時涵紊,會得到相應(yīng)的行號,至于什么時候分行幔摸、在哪里分行是由改類主動確定的摸柄,并不是在原始中有這樣一個行號。在輸出部分沒有對應(yīng)的部分既忆,我們完全可以自己建立一個LineNumberOutputStream驱负,在最初寫入時會有一個基準(zhǔn)的行號,以后每次遇到換行時會在下一行添加一個行號尿贫,看起來也是可以的电媳。好像更不入流了。
- PushbackInputStream 的功能是查看最后一個字節(jié)庆亡,不滿意就放入緩沖區(qū)。主要用在編譯器的語法捞稿、詞法分析部分又谋。輸出部分的BufferedOutputStream 幾乎實現(xiàn)相近的功能。
- StringBufferInputStream 已經(jīng)被Deprecated娱局,本身就不應(yīng)該出現(xiàn)在InputStream 部分彰亥,主要因為String 應(yīng)該屬于字符流的范圍。已經(jīng)被廢棄了衰齐,當(dāng)然輸出部分也沒有必要需要它了任斋!還允許它存在只是為了保持版本的向下兼容而已。
- SequenceInputStream 可以認為是一個工具類耻涛,將兩個或者多個輸入流當(dāng)成一個輸入流依次讀取废酷。完全可以從IO 包中去除,還完全不影響IO 包的結(jié)構(gòu)抹缕,卻讓其更“純潔”――純潔的Decorator 模式澈蟆。
- PrintStream 也可以認為是一個輔助工具。主要可以向其他輸出流卓研,或者FileInputStream 寫入數(shù)據(jù)趴俘,本身內(nèi)部實現(xiàn)還是帶緩沖的。本質(zhì)上是對其它流的綜合運用的一個工具而已奏赘。一樣可以踢出IO 包寥闪!System.out 和System.out 就是PrintStream 的實例!
4.字符輸入流Reader
- Reader 是所有的輸入字符流的父類磨淌,它是一個抽象類疲憋。
- CharReader、StringReader 是兩種基本的介質(zhì)流伦糯,它們分別將Char 數(shù)組柜某、String中讀取數(shù)據(jù)嗽元。PipedReader 是從與其它線程共用的管道中讀取數(shù)據(jù)。
- BufferedReader 很明顯就是一個裝飾器喂击,它和其子類負責(zé)裝飾其它Reader 對象剂癌。
- FilterReader 是所有自定義具體裝飾流的父類,其子類PushbackReader 對Reader 對象進行裝飾翰绊,會增加一個行號佩谷。
- InputStreamReader 是一個連接字節(jié)流和字符流的橋梁,它將字節(jié)流轉(zhuǎn)變?yōu)樽址骷嗍取ileReader 可以說是一個達到此功能谐檀、常用的工具類,在其源代碼中明顯使用了將FileInputStream 轉(zhuǎn)變?yōu)镽eader 的方法裁奇。我們可以從這個類中得到一定的技巧桐猬。Reader 中各個類的用途和使用方法基本和InputStream 中的類使用一致。后面會有Reader 與InputStream 的對應(yīng)關(guān)系刽肠。
5.字符輸出流Writer
- Writer 是所有的輸出字符流的父類溃肪,它是一個抽象類。
- CharArrayWriter音五、StringWriter 是兩種基本的介質(zhì)流惫撰,它們分別向Char 數(shù)組、String 中寫入數(shù)據(jù)躺涝。PipedWriter 是向與其它線程共用的管道中寫入數(shù)據(jù)厨钻。
- BufferedWriter 是一個裝飾器為Writer 提供緩沖功能。
- PrintWriter 和PrintStream 極其類似坚嗜,功能和使用也非常相似夯膀。
- OutputStreamWriter 是OutputStream 到Writer 轉(zhuǎn)換的橋梁,它的子類FileWriter 其實就是一個實現(xiàn)此功能的具體類(具體可以研究一SourceCode)惶傻。功能和使用和OutputStream 極其類似棍郎,后面會有它們的對應(yīng)圖。
6.字符流的輸入與輸出的對應(yīng)
7.字符流與字節(jié)流轉(zhuǎn)換
轉(zhuǎn)換流的特點:
- 其是字符流和字節(jié)流之間的橋梁
- 可對讀取到的字節(jié)數(shù)據(jù)經(jīng)過指定編碼轉(zhuǎn)換成字符
- 可對讀取到的字符數(shù)據(jù)經(jīng)過指定編碼轉(zhuǎn)換成字節(jié)
何時使用轉(zhuǎn)換流银室?
- 當(dāng)字節(jié)和字符之間有轉(zhuǎn)換動作時涂佃;
- 流操作的數(shù)據(jù)需要編碼或解碼時。
具體的對象體現(xiàn):
- InputStreamReader:字節(jié)到字符的橋梁
- OutputStreamWriter:字符到字節(jié)的橋梁
這兩個流對象是字符體系中的成員蜈敢,它們有轉(zhuǎn)換作用辜荠,本身又是字符流,所以在構(gòu)造的時候需要傳入字節(jié)流對象進來抓狭。
- OutputStreamWriter:字符到字節(jié)的橋梁
8.File類
File類是對文件系統(tǒng)中文件以及文件夾進行封裝的對象伯病,可以通過對象的思想來操作文件和文件夾。 File類保存文件或目錄的各種元數(shù)據(jù)信息,包括文件名午笛、文件長度惭蟋、最后修改時間、是否可讀药磺、獲取當(dāng)前文件的路徑名告组,判斷指定文件是否存在、獲得當(dāng)前目錄中的文件列表癌佩,創(chuàng)建木缝、刪除文件和目錄等方法。
9.RandomAccessFile類
該對象并不是流體系中的一員围辙,其封裝了字節(jié)流我碟,同時還封裝了一個緩沖區(qū)(字符數(shù)組),通過內(nèi)部的指針來操作字符數(shù)組中的數(shù)據(jù)姚建。 該對象特點:
- 該對象只能操作文件矫俺,所以構(gòu)造函數(shù)接收兩種類型的參數(shù):a.字符串文件路徑;b.File對象桥胞。
- 該對象既可以對文件進行讀操作恳守,也能進行寫操作,在進行對象實例化時可指定操作模式
下面來看一些具體的代碼例子:
按字節(jié)來讀取文件
public class ReadFromFile {
/**
* 以字節(jié)為單位讀取文件贩虾,常用于讀二進制文件,如圖片沥阱、聲音缎罢、影像等文件。
*/
public static void readFileByBytes(String fileName) {
File file = new File(fileName);
InputStream in = null;
try {
System.out.println("以字節(jié)為單位讀取文件內(nèi)容考杉,一次讀一個字節(jié):");
// 一次讀一個字節(jié)
in = new FileInputStream(file);
int tempbyte;
while ((tempbyte = in.read()) != -1) {
System.out.print(tempbyte);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
try {
System.out.println("以字節(jié)為單位讀取文件內(nèi)容策精,一次讀多個字節(jié):");
// 一次讀多個字節(jié)
byte[] tempbytes = new byte[100];
int byteread = 0;
in = new FileInputStream(fileName);
ReadFromFile.showAvailableBytes(in);
// 讀入多個字節(jié)到字節(jié)數(shù)組中,byteread為一次讀入的字節(jié)數(shù)
while ((byteread = in.read(tempbytes)) != -1) {
System.out.print(tempbytes, 0, byteread);
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
}
}
}
}
按字符來讀取文件
/**
* 以字符為單位讀取文件崇棠,常用于讀文本,數(shù)字等類型的文件
*/
public static void readFileByChars(String fileName) {
File file = new File(fileName);
Reader reader = null;
try {
System.out.println("以字符為單位讀取文件內(nèi)容,一次讀一個字符:");
// 一次讀一個字符
reader = new InputStreamReader(new FileInputStream(file));
int tempchar;
while ((tempchar = reader.read()) != -1) {
// 對于windows下弱恒,\r\n這兩個字符在一起時旦万,表示一個換行。
// 但如果這兩個字符分開顯示時萎坷,會換兩次行凹联。
// 因此,屏蔽掉\r哆档,或者屏蔽\n蔽挠。否則,將會多出很多空行瓜浸。
if (((char) tempchar) != '\r') {
System.out.print((char) tempchar);
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println("以字符為單位讀取文件內(nèi)容澳淑,一次讀多個字符:");
// 一次讀多個字符
char[] tempchars = new char[30];
int charread = 0;
//由于要以字符來讀取比原,所以需要套上字符流
reader = new InputStreamReader(new FileInputStream(fileName));
// 讀入多個字符到字符數(shù)組中,charread為一次讀取字符數(shù)
while ((charread = reader.read(tempchars)) != -1) {
// 同樣屏蔽掉\r不顯示
if ((charread == tempchars.length)
&& (tempchars[tempchars.length - 1] != '\r')) {
System.out.print(tempchars);
} else {
for (int i = 0; i < charread; i++) {
if (tempchars[i] == '\r') {
continue;
} else {
System.out.print(tempchars[i]);
}
}
}
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
按行來讀取文件
/**
* 以行為單位讀取文件杠巡,常用于讀面向行的格式化文件
*/
public static void readFileByLines(String fileName) {
File file = new File(fileName);
BufferedReader reader = null;
try {
System.out.println("以行為單位讀取文件內(nèi)容量窘,一次讀一整行:");
reader = new BufferedReader(new FileReader(file));
String tempString = null;
int line = 1;
// 一次讀入一行,直到讀入null為文件結(jié)束
while ((tempString = reader.readLine()) != null) {
// 顯示行號
System.out.println("line " + line + ": " + tempString);
line++;
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
將一個文件的內(nèi)容寫入另一個文件(按行來寫)
public class FileTest {
public static void main(String[] args) {
File file=new File("c:\\test.txt");
BufferedReader read=null;
BufferedWriter writer=null;
try {
writer=new BufferedWriter(new FileWriter("c:\\zwm.txt"));
} catch (IOException e1) {
e1.printStackTrace();
}
try {
read=new BufferedReader(new FileReader(file));
String tempString = null;
while((tempString=read.readLine())!=null){
writer.append(tempString);
writer.newLine();//換行
writer.flush();//需要及時清掉流的緩沖區(qū)忽孽,萬一文件過大就有可能無法寫入了
}
read.close();
writer.close();
System.out.println("文件寫入完成...");
} catch (IOException e) {
e.printStackTrace();
}
}
}