Java api 入门教程 之 JAVA的IO处理
IO是輸入和輸出的簡稱,在實際的使用時,輸入和輸出是有方向的。就像現(xiàn)實中兩個人之間借錢一樣,例如A借錢給B,相對于A來說是借出,而相對于B來說則是借入。所以在程序中提到輸入和輸出時,也需要區(qū)分清楚是相對的內(nèi)容。
在 程序中,輸入和輸出都是相對于當前程序而言的,例如從硬盤上讀取一個配置文件的內(nèi)容到程序中,則相當于將文件的內(nèi)容輸入到程序內(nèi)部,因此輸入和“讀”對 應(yīng),而將程序中的內(nèi)容保存到硬盤上,則相當于將文件的內(nèi)容輸出到程序外部,因此輸出和“寫”對應(yīng)。熟悉輸入和輸出的對應(yīng)關(guān)系,將有助于后續(xù)內(nèi)容的學(xué)習(xí)。
?
在Java語言中,輸入和輸出的概念要比其它語言的輸入和輸出的概念涵蓋的內(nèi)容廣泛得多,不僅包含文件的讀寫,也包含網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送,甚至內(nèi)存數(shù)據(jù)的讀寫以及控制臺數(shù)據(jù)的接收等都由IO來完成。
為了使輸入和輸出的結(jié)構(gòu)保持統(tǒng)一,從而方便程序員使用IO相關(guān)的類,在Java語言的IO類設(shè)計中引入了一個新的概念——Stream(流)。
?
由于在進行IO操作時,需要操作的種類很多,例如文件、內(nèi)存和網(wǎng)絡(luò)連接等,這些都被稱作數(shù)據(jù)源(data source),對于不同的數(shù)據(jù)源處理的方式是不一樣的,如果直接交給程序員進行處理,對于程序員來說則顯得比較復(fù)雜。所以在所有的IO類設(shè)計時,在讀數(shù)據(jù)時,JDK API將數(shù)據(jù)源的數(shù)據(jù)轉(zhuǎn)換為一種固定的數(shù)據(jù)序列,在寫數(shù)據(jù)時,將需要寫的數(shù)據(jù)以一定的格式寫入到數(shù)據(jù)序列,由JDK API完成將數(shù)據(jù)序列中的數(shù)據(jù)寫入到對應(yīng)的數(shù)據(jù)源中。這樣由系統(tǒng)完成復(fù)雜的數(shù)據(jù)轉(zhuǎn)換以及不同數(shù)據(jù)源之間的不同的變換,從而簡化程序員的編碼。
IO的 這種設(shè)計就和城市中的供水和排水系統(tǒng)設(shè)計是一樣的,在供水的時候,水源有江河水、湖水和地下水等不同類型,由自來水公司完成把水源轉(zhuǎn)換為對應(yīng)的水流。而在 排水系統(tǒng)設(shè)計時,只需要將污水排入污水管道即可,至于這些污水是怎么被處理的,則不需要關(guān)心,這樣也簡化了家庭用水的處理。
IO設(shè)計中這種數(shù)據(jù)序列被形象的稱作流(Stream)。通過使用流的概念,使程序員面對不同的數(shù)據(jù)源時只需要建立不同的流即可,而底層流實現(xiàn)的復(fù)雜性則由系統(tǒng)完成,從而使程序員不必深入的了解每種數(shù)據(jù)源的讀寫方式,從而降低了IO編程的復(fù)雜度。
在整個IO處理中,讀數(shù)據(jù)的過程分為兩個步驟:
1、將數(shù)據(jù)源的內(nèi)容轉(zhuǎn)換為流結(jié)構(gòu),該步驟由JDK API完成,程序員只需要選擇合適的流類型即可。
2、從流中讀取數(shù)據(jù),該步驟由程序員完成,流中數(shù)據(jù)的順序和數(shù)據(jù)源中數(shù)據(jù)的存儲順序保持一致。
?????????
寫數(shù)據(jù)的過程也分為兩個步驟:
1、為連接指定的數(shù)據(jù)源而建立的專門的流結(jié)構(gòu),該步驟由JDK API完成,程序員只需要選擇合適的流類型即可。
2、將數(shù)據(jù)以一定的格式寫入到流中,該步驟由程序員完成,寫入流中的數(shù)據(jù)的順序就是數(shù)據(jù)在數(shù)據(jù)源中的存儲順序。
最后,當數(shù)據(jù)寫入流中以后,可以通過一定的方式把流中的數(shù)據(jù)寫入數(shù)據(jù)源,或者當流被關(guān)閉時,系統(tǒng)會自動將流中的數(shù)據(jù)寫入數(shù)據(jù)源中。
?
這樣,在整個IO類設(shè)計時,將最復(fù)雜的和數(shù)據(jù)源操作的部分由JDK API進行完成,而程序員進行編程時,只需要選擇合適的流類型,然后進行讀寫即可。和現(xiàn)實的結(jié)構(gòu)一樣,IO中的流也是有方向的,用于讀的流被稱作輸入流(Input Stream),用于寫的流被稱作輸出流(Output Stream)。則進行讀寫的時候需要選擇合適的流對象進行操作。
由于Java語言使用面向?qū)ο蠹夹g(shù),所以在實現(xiàn)時,每個流類型都使用專門的類進行代表,而把讀或?qū)懺擃愋蛿?shù)據(jù)源的邏輯封裝在類的內(nèi)部,在程序員實際使用時創(chuàng)建對應(yīng)的對象就完成了流的構(gòu)造,后續(xù)的IO操作則只需要讀或?qū)懥鲗ο髢?nèi)部的數(shù)據(jù)即可。這樣IO操作對于Java程序員來說,就顯得比較簡單,而且比較容易操作了。
?
?
I/O類體系
?在JDK API中,基礎(chǔ)的IO類都位于java.io包,而新實現(xiàn)的IO類則位于一系列以java.nio開頭的包名中,這里首先介紹java.io包中類的體系結(jié)構(gòu)。
?按照前面的說明,流是有方向的,則整個流的結(jié)構(gòu)按照流的方向可以劃分為兩類:
?
1、輸入流:
該類流將外部數(shù)據(jù)源的數(shù)據(jù)轉(zhuǎn)換為流,程序通過讀取該類流中的數(shù)據(jù),完成對于外部數(shù)據(jù)源中數(shù)據(jù)的讀入。
?
2、輸出流:
該類流完成將流中的數(shù)據(jù)轉(zhuǎn)換到對應(yīng)的數(shù)據(jù)源中,程序通過向該類流中寫入數(shù)據(jù),完成將數(shù)據(jù)寫入到對應(yīng)的外部數(shù)據(jù)源中。
而在實際實現(xiàn)時,由于JDK API歷史的原因,在java.io包中又實現(xiàn)了兩類流:字節(jié)流(byte stream)和字符流(char stream)。這兩種流實現(xiàn)的是流中數(shù)據(jù)序列的單位,在字節(jié)流中,數(shù)據(jù)序列以byte為單位,也就是流中的數(shù)據(jù)按照一個byte一個byte的順序?qū)崿F(xiàn)成流,對于該類流操作的基本單位是一個byte,而對于字節(jié)流,數(shù)據(jù)序列以char為單位,也就是流中的數(shù)據(jù)按照一個char一個插入的順序?qū)崿F(xiàn)成流,對于該類流操作的基本單位是一個char。
另外字節(jié)流是從JDK1.0開始加入到API中的,而字符流則是從JDK1.1開始才加入到API中的,對于現(xiàn)在使用的JDK版本來說,這兩類流都包含在API的內(nèi)部。在實際使用時,字符流的效率要比字節(jié)流高一些。
在實際使用時,字符流中的類基本上和字節(jié)流中的類對應(yīng),所以在開始學(xué)習(xí)IO類時,可以從最基礎(chǔ)的字節(jié)流開始學(xué)習(xí)。
在SUN設(shè)計JDK的IO類時,按照以上的分類,為每個系列的類設(shè)計了一個父類,而實現(xiàn)具體操作的類都作為該系列類的子類,則IO類設(shè)計時的四個體系中每個體系中對應(yīng)的父類分別是:
?
?字節(jié)輸入流InputStream
該類是IO編程中所有字節(jié)輸入流的父類,熟悉該類的使用將對使用字節(jié)輸入流產(chǎn)生很大的幫助,下面做一下詳細的介紹。
按照前面介紹的流的概念,字節(jié)輸入流完成的是按照字節(jié)形式構(gòu)造讀取數(shù)據(jù)的輸入流的結(jié)構(gòu),每個該類的對象就是一個實際的輸入流,在構(gòu)造時由API完成將外部數(shù)據(jù)源轉(zhuǎn)換為流對象的操作,這種轉(zhuǎn)換對程序員來說是透明的。在程序使用時,程序員只需要讀取該流對象,就可以完成對于外部數(shù)據(jù)的讀取了。
InputStream是所有字節(jié)輸入流的父類,所以在InputStream類中包含的每個方法都會被所有字節(jié)輸入流類繼承,通過將讀取以及操作數(shù)據(jù)的基本方法都聲明在InputStream類內(nèi)部,使每個子類根據(jù)需要覆蓋對應(yīng)的方法,這樣的設(shè)計可以保證每個字節(jié)輸入流子類在進行實際使用時,開放給程序員使用的功能方法是一致的。這樣將簡化IO類學(xué)習(xí)的難度,方便程序員進行實際的編程。
默認情況下,對于輸入流內(nèi)部數(shù)據(jù)的讀取都是單向的,也就是只能從輸入流從前向后讀,已經(jīng)讀取的數(shù)據(jù)將從輸入流內(nèi)部刪除掉。如果需要重復(fù)讀取流中同一段內(nèi)容,則需要使用流類中的mark方法進行標記,然后才能重復(fù)讀取。這種設(shè)計在使用流類時,需要深刻進行體會。
?
在InputStream類中,常見的方法有:
?
a、available方法
?
public int available() throws IOException
該方法的作用是返回當前流對象中還沒有被讀取的字節(jié)數(shù)量。也就是獲得流中數(shù)據(jù)的長度。
假設(shè)初始情況下流內(nèi)部包含100個字節(jié)的數(shù)據(jù),程序調(diào)用對應(yīng)的方法讀取了一個字節(jié),則當前流中剩余的字節(jié)數(shù)量將變成99個。
另外,該方法不是在所有字節(jié)輸入流內(nèi)部都得到正確的實現(xiàn),所以使用該方法獲得流中數(shù)據(jù)的個數(shù)是不可靠的。
?
b、close方法
?
public void close() throws IOException
該方法的作用是關(guān)閉當前流對象,并釋放該流對象占用的資源。
在IO操作結(jié)束以后,關(guān)閉流是進行IO操作時都需要實現(xiàn)的功能,這樣既可以保證數(shù)據(jù)源的安全,也可以減少內(nèi)存的占用。
?
c、markSupported方法
?
public boolean markSupported()
該方法的作用是判斷流是否支持標記(mark)。標記類似于讀書時的書簽,可以很方便的回到原來讀過的位置繼續(xù)向下讀取。
?
d、reset方法
public void reset() throws IOException
該方法的作用是使流讀取的位置回到設(shè)定標記的位置。可以從該位置開始繼續(xù)向后讀取。
?
e、mark方法
public void mark(int readlimit)
為流中當前的位置設(shè)置標志,使得以后可以從該位置繼續(xù)讀取。變量readlimit指設(shè)置該標志以后可以讀取的流中最大數(shù)據(jù)的個數(shù)。當設(shè)置標志以后,讀取的字節(jié)數(shù)量超過該限制,則標志會失效。
?
f、read方法
read方法是輸入流類使用時最核心的方法,能夠熟練使用該方法就代表IO基本使用已經(jīng)入門。所以在學(xué)習(xí)以及后期的使用中都需要深刻理解該方法的使用。
在實際讀取流中的數(shù)據(jù)時,只能按照流中的數(shù)據(jù)存儲順序依次進行讀取,在使用字節(jié)輸入流時,讀取數(shù)據(jù)的最小單位是字節(jié)(byte)。
另外,需要注意的是,read方法是阻塞方法,也就是如果流對象中無數(shù)據(jù)可以讀取時,則read方法會阻止程序繼續(xù)向下運行,一直到有數(shù)據(jù)可以讀取為止。
read方法總計有三個,依次是:
?
?public abstract int read() throws IOException
該方法的作用是讀取當前流對象中的第一個字節(jié)。當該字節(jié)被讀取出來以后,則該字節(jié)將被從流對象中刪除,原來流對象中的第二個字節(jié)將變成流中的第一個字節(jié),而使用流對象的available方法獲得的數(shù)值也將減少1。如果需要讀取流中的所以數(shù)據(jù),只要使用一個循環(huán)依次讀取每個數(shù)據(jù)即可。當讀取到流的末尾時,該方法返回-1。該返回值的int中只有最后一個字節(jié)是流中的有效數(shù)據(jù),所以在獲得流中的數(shù)值時需要進行強制轉(zhuǎn)換。返回值作成int的目的主要是處理好-1的問題。
由于該方法是抽象的,所以會在子類中被覆蓋,從而實現(xiàn)最基礎(chǔ)的讀數(shù)據(jù)的功能。
?
public int read(byte[] b) throws IOException
該方法的作用是讀取當前流對象中的數(shù)據(jù),并將讀取到的數(shù)據(jù)依次存儲到數(shù)組b(b需要提前初始化完成)中,也就是把當前流中的第一個字節(jié)的數(shù)據(jù)存儲到b[0],第二個字節(jié)的數(shù)據(jù)存儲到b[1],依次類推。流中已經(jīng)讀取過的數(shù)據(jù)也會被刪除,后續(xù)的數(shù)據(jù)會變成流中的第一個字節(jié)。而實際讀取的字節(jié)數(shù)量則作為方法的返回值返回。
?
public int read(byte[] b, int off, int len) throws IOException
該方法的作用和上面的方法類似,也是將讀取的數(shù)據(jù)存儲到b中,只是將流中的第一個數(shù)據(jù)存儲到b中下標為off的位置,最多讀取len個數(shù)據(jù),而實際讀取的字節(jié)數(shù)量則作為方法的返回值返回。
?
g、skip方法
public long skip(long n) throws IOException
該方法的作用是跳過當前流對象中的n個字節(jié),而實際跳過的字節(jié)數(shù)量則以返回值的方式返回。
跳過n個字節(jié)以后,如果需要讀取則是從新的位置開始讀取了。使用該方法可以跳過流中指定的字節(jié)數(shù),而不用依次進行讀取了。
?????????從流中讀取出數(shù)據(jù)以后,獲得的是一個byte數(shù)組,還需要根據(jù)以前的數(shù)據(jù)格式,實現(xiàn)對于該byte數(shù)組的解析。
?????????由于InputStream類是字節(jié)輸入流的父類,所以該體系中的每個子類都包含以上的方法,這些方法是實現(xiàn)IO流數(shù)據(jù)讀取的基礎(chǔ)。
?
??字節(jié)輸出流OutputStream
?????????該類是所有的字節(jié)輸出流的父類,在實際使用時,一般使用該類的子類進行編程,但是該類內(nèi)部的方法是實現(xiàn)字節(jié)輸出流的基礎(chǔ)。
?????????該體系中的類完成把對應(yīng)的數(shù)據(jù)寫入到數(shù)據(jù)源中,在寫數(shù)據(jù)時,進行的操作分兩步實現(xiàn):第一步,將需要輸出的數(shù)據(jù)寫入流對象中,數(shù)據(jù)的格式由程序員進行設(shè)定,該步驟需要編寫代碼實現(xiàn);第二步,將流中的數(shù)據(jù)輸出到數(shù)據(jù)源中,該步驟由API實現(xiàn),程序員不需要了解內(nèi)部實現(xiàn)的細節(jié),只需要構(gòu)造對應(yīng)的流對象即可。
?????????在實際寫入流時,流內(nèi)部會保留一個緩沖區(qū),會將程序員寫入流對象的數(shù)據(jù)首先暫存起來,然后在緩沖區(qū)滿時將數(shù)據(jù)輸出到數(shù)據(jù)源。當然,當流關(guān)閉時,輸出流內(nèi)部的數(shù)據(jù)會被強制輸出。
?????????字節(jié)輸出流中數(shù)據(jù)的單位是字節(jié),在將數(shù)據(jù)寫入流時,一般情況下需要將數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組進行寫入。
?
?????????在OutputStream中,常見的方法有:
a、close方法
public void close() throws IOException
該方法的作用是關(guān)閉流,釋放流占用的資源。
?
b、flush方法
public void flush() throws IOException
該方法的作用是將當前流對象中的緩沖數(shù)據(jù)強制輸出出去。使用該方法可以實現(xiàn)立即輸出。
?
c、write方法
write方法是輸出流中的核心方法,該方法實現(xiàn)將數(shù)據(jù)寫入流中。在實際寫入前,需要實現(xiàn)對應(yīng)的格式,然后依次寫入到流中。寫入流的順序就是實際數(shù)據(jù)輸出的順序。
write方法總計有3個,依次是:
public abstract void write(int b) throws IOException
該方法的作用是向流的末尾寫入一個字節(jié)的數(shù)據(jù)。寫入的數(shù)據(jù)為參數(shù)b的最后一個字節(jié)。在實際向流中寫數(shù)據(jù)時需要按照邏輯的順序進行寫入。該方法在OutputStream的子類內(nèi)部進行實現(xiàn)。
public void write(byte[] b) throws IOException
該方法的作用是將數(shù)組b中的數(shù)據(jù)依次寫入當前的流對象中。
public void write(byte[] b, int off, int len) throws IOException
該方法的作用是將數(shù)組b中從下標為off(包含)開始,后續(xù)長度為len個的數(shù)據(jù)依次寫入到流對象中。
在實際寫入時,還需要根據(jù)邏輯的需要設(shè)定byte數(shù)值的格式,這個根據(jù)不同的需要實現(xiàn)不同的格式。
?
?字符輸入流Reader
?????????字 符輸入流體系是對字節(jié)輸入流體系的升級,在子類的功能上基本和字節(jié)輸入流體系中的子類一一對應(yīng),但是由于字符輸入流內(nèi)部設(shè)計方式的不同,使得字符輸入流的 執(zhí)行效率要比字節(jié)輸入流體系高一些,在遇到類似功能的類時,可以優(yōu)先選擇使用字符輸入流體系中的類,從而提高程序的執(zhí)行效率。
???????? Reader體系中的類和InputStream體系中的類,在功能上是一致的,最大的區(qū)別就是Reader體系中的類讀取數(shù)據(jù)的單位是字符(char),也就是每次最少讀入一個字符(兩個字節(jié))的數(shù)據(jù),在Reader體系中的讀數(shù)據(jù)的方法都以字符作為最基本的單位。
???????? Reader類和InputStream類中的很多方法,無論聲明還是功能都是一樣的,但是也增加了兩個方法,依次介紹如下:
?
a、read方法
public int read(CharBuffer target) throws IOException
該方法的作用是將流內(nèi)部的數(shù)據(jù)依次讀入CharBuffer對象中,實際讀入的char個數(shù)作為返回值返回。
?
b、ready方法
public boolean ready() throws IOException
該方法的作用是返回當前流對象是否準備完成,也就是流內(nèi)部是否包含可以被讀取的數(shù)據(jù)。
?
其它和InputStream類一樣的方法可以參看上面的介紹。
?
?
?字符輸出流Writer
?????????字 符輸出流體系是對字節(jié)輸出流體系的升級,在子類的功能實現(xiàn)上基本上和字節(jié)輸出流保持一一對應(yīng)。但由于該體系中的類設(shè)計的比較晚,所以該體系中的類執(zhí)行的效 率要比字節(jié)輸出流中對應(yīng)的類效率高一些。在遇到類似功能的類時,可以優(yōu)先選擇使用該體系中的類進行使用,從而提高程序的執(zhí)行效率。
???????? Writer體系中的類和OutputStream體系中的類,在功能上是一致的,最大的區(qū)別就是Writer體系中的類寫入數(shù)據(jù)的單位是字符(char),也就是每次最少寫入一個字符(兩個字節(jié))的數(shù)據(jù),在Writer體系中的寫數(shù)據(jù)的方法都以字符作為最基本的操作單位。
???????? Writer類和OutputStream類中的很多方法,無論聲明還是功能都是一樣的,但是還是增加了一些方法,依次介紹如下:
?
a、append方法
將數(shù)據(jù)寫入流的末尾。總計有3個方法,依次是:
?public Writer append(char c) throws IOException
該方法的作用和write(int c)的作用完全一樣,既將字符c寫入流的末尾。
public Writer append(CharSequence csq) throws IOException
該方法的作用是將CharSequence對象csq寫入流的末尾,在寫入時會調(diào)用csq的toString方法將該對象轉(zhuǎn)換為字符串,然后再將該字符串寫入流的末尾。
public Writer append(CharSequence csq, int start, int end)throws IOException
該方法的作用和上面的方法類似,只是將轉(zhuǎn)換后字符串從索引值為start(包含)到索引值為end(不包含)的部分寫入流中。
?
b、write方法
?除了基本的write方法以外,在Writer類中又新增了兩個,依次是:
public void write(String str) throws IOException
該方法的作用是將字符串str寫入流中。寫入時首先將str使用getChars方法轉(zhuǎn)換成對應(yīng)的char數(shù)組,然后實現(xiàn)依次寫入流的末尾。
public void write(String str, int off, int len)throws IOException
該方法的作用是將字符串str中索引值為off(包含)開始,后續(xù)長度為len個字符寫入到流的末尾。
使用這兩個方法將更方便將字符串寫入流的末尾。
?????????
其它和OutputStream類一樣的方法可以參看上面的介紹。
?
?小結(jié)
?????????在實際使用IO類時,根據(jù)邏輯上的需要,挑選對應(yīng)體系中的類進行實際的使用,從而實現(xiàn)程序中IO的相關(guān)功能。
?????????熟悉了IO類的體系以后,就可以首先熟悉基本的IO類的使用,然后再按照IO類體系中相關(guān)類的使用方式逐步去了解相關(guān)的IO類的使用,從而逐步熟悉java.io包中類的使用,然后再掌握IO編程。
?????????在實際使用時,一般都使用這4個類中對應(yīng)的子類,每個子類完成相關(guān)的功能。對于這些子類,也可以根據(jù)這些類是否直接連接數(shù)據(jù)源,將這些IO類分類為:
1、實體流
? ? ? ?指直接連接數(shù)據(jù)源的IO流類
2、裝飾流
? ? ? ?指不直接連接數(shù)據(jù)源,而是建立在其它實體流對象的基礎(chǔ)之上。
? ? ? ?
?
轉(zhuǎn)載于:https://www.cnblogs.com/liuhongfeng/p/5175109.html
總結(jié)
以上是生活随笔為你收集整理的Java api 入门教程 之 JAVA的IO处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VMware上实现LVS负载均衡(NAT
- 下一篇: 胶带多少钱啊?