1. 流 Stream
Java中(絕大部分編程語言類似)文件一般不是單獨(dú)處理芬膝,而是視為輸入輸出(I/O)設(shè)備的一種。
Java使用統(tǒng)一的概念來處理所有的IO菇晃。包括鍵盤祈远,終端呆万,網(wǎng)絡(luò)等。
- JavaIO的基本類位于java.io包下车份。
Stream | 含義 |
---|---|
InputStream | 表示輸入流 |
OutputStream | 表示輸出流 |
FileInputStream | 表示文件輸入流 |
FileOutputStream | 表示文件輸出流 |
有了流的概念,就有了很多面向流的函數(shù)谋减,輸入輸出都是抽象的流,提供流的加密扫沼、壓縮等功能出爹。
一些實(shí)際上不是IO的數(shù)據(jù)源和目的地也轉(zhuǎn)換成了流,以便參與流的整體概念體系的協(xié)作充甚。
例如以政,字節(jié)數(shù)組ByteArrayInputStream/ByteArrayOutputStream
2. 裝飾器設(shè)計(jì)模式
基本的流按照字節(jié)讀寫霸褒,沒有緩沖區(qū)伴找,性能底下。Java使用裝飾器模式引入很多裝飾類對基本的流增加功能废菱。
一個(gè)裝飾類一般只關(guān)注一個(gè)方面的功能技矮。所以實(shí)際使用中經(jīng)常需要使用多個(gè)裝飾類抖誉。
Java中有很多裝飾類。對流提供過濾功能有兩個(gè)基類:FilterInputStream/FilterOutputSteam.
過濾功能的裝飾器對流提供一些功能包裝衰倦,輸入輸出都是流袒炉。
流的裝飾器類成對出現(xiàn),分別對應(yīng)輸入和輸出流樊零。
FilterInputStream/FilterOutputSteam有很多子類我磁,這里只列出出入的子類,輸出的子類類似:
FilterInputStream的子類 | 功能 |
---|---|
BufferedInputStream | 對流起緩沖作用 |
DataInputStream | 按照8種基本類型對流讀寫 |
GZipInputStream | 對流提供壓縮功能 |
ZipInputStream | 對流提供壓縮功能 |
PrintStream | 將基本類型驻襟,對象輸出為字符串表示 |
3. Reader/Writer
以InputStream/OutputStream 為基類的流基本都是以二進(jìn)制形式處理數(shù)據(jù)夺艰。
不能方便的處理文本文件,沒有編碼概念沉衣。能方便的按字符處理文本的基類是Reader/Writer郁副。
這兩個(gè)類也有很多子類。
Reader/Writer 的子類 | 功能 |
---|---|
FileReader/FileWriter | 讀/寫文件 |
BufferedReader/BufferedWriter | 緩沖讀/寫 |
CharArrayReader/CharArrayWriter | 將字符數(shù)組包裝成流 |
StringReader/StringWriter | 將字符串包裝成流 |
InputStreamReader/InputStreamWriter | 轉(zhuǎn)換InputStream/OutputStream和Reader/Writer |
PrintWrite | 輸出Write中的基本類型豌习,對象輸出為其字符串表示 |
大部分情況下存谎,使用流或者Reader/Writer讀寫文件內(nèi)容。但Java還提供樂一個(gè)獨(dú)立的可以隨機(jī)讀寫文件的類RandomAccessFile肥隆。用于大小已知的文件既荚。開發(fā)中用的較少。
一些系統(tǒng)程序中用的較多栋艳。
以上介紹的類都是操作數(shù)據(jù)本身固以,而數(shù)據(jù)保存的載體,Java使用File類來表示嘱巾,提供文件路徑憨琳,文件元數(shù)據(jù),臨時(shí)文件旬昭,權(quán)限管理等功能篙螟。
4. Nio
上面的類都是位于java.io
包下,Java中還有一個(gè)關(guān)于IO的操作包java.nio
,nio
的意思是New IO问拘。
它有緩沖區(qū)和通道的概念遍略。
緩沖和通道概念更像是操作系統(tǒng)的概念。利用緩沖區(qū)和通道往往可以達(dá)成和流類似的目的骤坐,某些操作性能也更高绪杏,
例如:通道可以利用操作系統(tǒng)和硬件提供的DMA(direct memory access 直接內(nèi)存存取)機(jī)制直接將數(shù)據(jù)從硬盤復(fù)制到網(wǎng)卡(不需要程序和CPU的參與)。
NIO還支持一些比較底層的功能纽绍。如 內(nèi)存映射文件(常用)蕾久、文件加鎖、自定義文件系統(tǒng)拌夏、非阻塞式IO,異步IO等僧著。
5. Serialization/Deserialization 序列化 反序列化
Serialization 序列化:是指將內(nèi)存中的java對象持久的保存到一個(gè)流中履因。
Deserialization 反序列:是指將流中的對象恢復(fù)到j(luò)ava內(nèi)存。
一般有兩個(gè)用途:一個(gè)是對象持久化盹愚;另一個(gè)是網(wǎng)絡(luò)遠(yuǎn)程調(diào)用栅迄。
Java主要通過interface Serializable
和class ObjectInputStream/ObjectOutputStream
提供對序列化的支持。
Java的默認(rèn)序列化有一些缺點(diǎn):序列化后的形式較大浪費(fèi)空間皆怕,序列化和反序列化的性能較低毅舆,java獨(dú)有的格式,不能與其他語言交互愈腾。
5.1 Xml/Json
Java對象可以序列化為xml格式,xml格式比較笨重朗兵,現(xiàn)在更多的使用Json格式。
XML/JSON都是文本格式顶滩,便于人閱讀余掖,但占用空間相對較大。在只用于網(wǎng)絡(luò)遠(yuǎn)程調(diào)用的情況下礁鲁,有很多其他的更高效的格式盐欺,比如ProtoBuf、Thrift,MessagePack.
其中仅醇,MessagePack是二進(jìn)制形式的JSON,更小更快冗美。
6. 二進(jìn)制文件和字節(jié)流
二進(jìn)制讀寫流的類的有:
類名 | 說明 |
---|---|
InputStream/OutputStream | 抽象基類 |
FileInputStream/FileOutputStream | 輸入源和輸出目標(biāo)是文件的流 |
ByteArrayInputStream/ByteArrayOutputStream | 輸入源和輸出目標(biāo)是字節(jié)數(shù)組的流 |
DataInputStream/DateOutputStream | 裝飾類,按照基本類型和字符串而非是字節(jié)讀寫流 |
BufferedInputStream/BufferedOutputStream | 裝飾類析二,對輸入輸出流提供緩沖功能 |
6.1 InputStream/OutputStream 抽象基類
先介紹InputStream
6.1.1 InputStream的主要方法是 public abstract int read() throw IOException
粉洼。
該抽象方法的一般實(shí)現(xiàn)是從流中讀取下一個(gè)字節(jié),返回取值范圍是-1,0~255的int類型叶摄。
當(dāng)讀取到流的結(jié)尾時(shí)返回-1
,如果流中沒有數(shù)據(jù)属韧,read()
方法會阻塞知道數(shù)據(jù)到來、流關(guān)閉或異常出現(xiàn)蛤吓。
6.1.2 InputStream還有 public int read(byte b[]) throws IOException
方法宵喂,可以一次性讀取多個(gè)字節(jié)。
讀入的字節(jié)放入?yún)?shù)數(shù)組
b[]
中会傲。返回讀入的實(shí)際字節(jié)個(gè)數(shù)锅棕。實(shí)際字節(jié)個(gè)數(shù)小于(流讀完了而沒有讀滿)等于數(shù)組b的長度。
如果剛開始讀取就讀到末尾則返回-1
淌山。否則該方法會盡力讀取至少一個(gè)字節(jié)裸燎,如果流中沒有字節(jié)則阻塞。非抽象方法泼疑,有默認(rèn)實(shí)現(xiàn):循環(huán)讀取一個(gè)字節(jié)的read方法德绿,但是子類的實(shí)現(xiàn)一邊更為高效。
流讀取結(jié)束后都應(yīng)該調(diào)用
close()
方法關(guān)閉釋放資源。所以一般放入finally語句中脆炎。
close()
方法自身也可能拋出異常梅猿,但通趁ダ保可以捕獲并忽略秒裕。
6.1.3 InputStream中還定義了如下方法:
1. public long skip(long n) throws IOException
2. public int available() throws IOException
3. public synchronized void mark(int readlimit)
4. public boolean markSupported()
5. public synchronized void reset() throws IOException
-
skip(long n)
方法跳過輸入流中的n個(gè)字節(jié),返回實(shí)際跳過的字節(jié)個(gè)數(shù)(流中剩余的字節(jié)個(gè)數(shù)可能小于n). -
avaiable()
返回下一次不需要阻塞就能讀取到的大概字節(jié)個(gè)數(shù),一般用在從網(wǎng)絡(luò)讀取數(shù)據(jù)時(shí)等到有足夠數(shù)據(jù)才去讀钞啸,防止阻塞几蜻。
一般的流都是一次性讀取的,且只能按照輸出的這一個(gè)方向讀取体斩,但是有時(shí)候希望能夠先看下后面的內(nèi)容梭稚,根據(jù)情況再重新讀取。
比如處理一個(gè)未知的二進(jìn)制文件絮吵,不確定其類型弧烤,我們可能可以通過流的前幾十個(gè)字節(jié)判斷其類型。然后重置到流開頭蹬敲。交給相應(yīng)的代碼處理暇昂。
InputStream定義了三個(gè)方法,用于支持從讀過的流中重復(fù)讀劝槲恕:mark reset markSupported
具體步驟是
- 先使用
mark(int readlimit)
將當(dāng)前位置標(biāo)記下來急波,readlimit表示往后可以讀取的最多字節(jié)數(shù)。讀取一些字節(jié)后(需要小于readlimit瘪校,否則標(biāo)記失效)澄暮。 - 調(diào)用
reset()
方法重置到mark標(biāo)記的位置,
不是所有的流都支持mark reset方法阱扬。是否支持可以通過調(diào)用markSupported方法查看返回值確認(rèn)泣懊。
InputStream 默認(rèn)實(shí)現(xiàn)是不支持的,F(xiàn)ileInputStream不支持麻惶,但BufferedInputStream
和ByteArrayInputStream
支持嗅定。
接下來介紹 OutputStream
6.1.4 OutputStream
的基本(主要)方法是 public abstract void write(int b) throws IOException
向六中寫入一個(gè)Byte字節(jié),參數(shù)雖然是int,但只會用到最低的8bit(1Byte=8bit).
6.1.5 OutputStream
還有兩個(gè)批量寫入的方法
public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException
第二個(gè)方法用踩,第一個(gè)寫入的字節(jié)是b[off],寫入個(gè)數(shù)len,最后一個(gè)是b[off+len-1].
6.1.6 OutputStream
還有兩個(gè)方法 flush close
public void flush() throws IOException
public void close() throws IOException
flush()
方法只對有緩沖的流起作用渠退,其將流中緩沖而未實(shí)際寫入的數(shù)據(jù)進(jìn)行實(shí)際寫入。
比如在BufferedOutputStream
中脐彩,調(diào)用flush方法會將其緩沖區(qū)的內(nèi)容寫到其裝飾的流中碎乃,并調(diào)用該流的flush方法。
基類OutputStream沒有緩沖惠奸,flush方法為空梅誓。
FileOutputStream沒有緩沖,沒有flush方法。
close()
方法一般會先調(diào)用flush()
方法梗掰,然后再釋放占用的資源嵌言。
同InputOutStream
一樣,close()
方法應(yīng)該放在finally塊中及穗。