您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“如何使用Java IO”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何使用Java IO”吧!
前言:
對(duì)程序語言的設(shè)計(jì)者來說,創(chuàng)建一個(gè)好的輸入/輸出 (I/O) 系統(tǒng)是一項(xiàng)艱難的任務(wù)
Java IO:即 Java 輸入/輸出系統(tǒng)。大部分程序都需要處理一些輸入,并由輸入產(chǎn)生一些輸出,因此Java為我們提供了 java.io 包
作為一個(gè)合格的程序開發(fā)者,說到 IO 我們并不會(huì)陌生,JAVA IO 系統(tǒng)的知識(shí)體系如下:
看完以上的圖,才會(huì)恍然,原來 Java.io 包中為我們提供了這么多支持。而我們恍然的同時(shí)也不必感到驚慌,俗話說萬變不離其宗,我們只需要根據(jù)源頭進(jìn)行擴(kuò)展,相信就可以很好的掌握IO知識(shí)體系。
File類
讀寫操作少不了與文件(File)打交道,因此我們想要掌握好 IO 流,不妨先從文件入手。
文件(File)這個(gè)詞即非單數(shù)也非復(fù)數(shù),它既能代表一個(gè)特殊的文件,又能表示一個(gè)目錄下的文件集。
列表
File 如果表示的是一個(gè)目錄下的文件集的時(shí)候,我們想要得到一個(gè)目錄可以怎么做?
File已經(jīng)為我們準(zhǔn)備好了 API,根據(jù)返回值類型,我們不難猜到每個(gè) API 方法的用處。
已知我們 D 盤目錄下有個(gè) TestFile 文件夾,該文件夾下有以下文件:
名稱列表
如果我們想要獲取指定目錄下的名稱列表,我們可以使用這兩個(gè)API:
list()
list(FilenameFilter filter)
不帶參數(shù)的 list() 方法默認(rèn)是列出指定目錄下的所有文件名稱。如果我們想要指定名稱的目錄名稱列表我們便可以使用另一個(gè)方法:
我們期望獲取帶有test關(guān)鍵字的文件名稱,而結(jié)果也如我們所愿。
文件列表
有時(shí)候我們的很多操作不單單針對(duì)于某個(gè)文件,而是在整個(gè)文件集上做操作。要產(chǎn)生這個(gè)文件集,那我們就需要借助 File 的另外API方法了:
listFiles()
listFiles(FilenameFilter filter)
listFiles(FileFilter filter)
有了以上經(jīng)驗(yàn),我們不難猜到 listFiles() 的作用便是列出所有的文件列表:
圖中我們已經(jīng)獲取到了文件集,該方法會(huì)返回的同樣是一個(gè)數(shù)組,不過是一個(gè) File類型的數(shù)組。
聰明的你肯定也已經(jīng)知道了如果獲取帶指定關(guān)鍵字的文件集
與上述列出文件名稱如出一轍,真是個(gè)小機(jī)靈鬼~
但是listFiles(FileFilter filter) 這個(gè)方法傳遞的參數(shù)與上有何異?我們不妨一試:
同樣是一個(gè)接口,同樣需要重寫 accept() 方法,但是這個(gè)方法只有一個(gè) File 的參數(shù)。因此這兩個(gè)參數(shù)都是用于文件過濾的,功能大同小異~
目錄工具
創(chuàng)建目錄
File 類的好用之處不僅能讓你對(duì)于已有目錄文件的操作,還能讓你無中生有!
文件的特性無外乎:名稱,大小,最后修改日期,可讀/寫,類型等
那么我們通過 API 也理應(yīng)能夠獲得:
以上什么類型都獲取到了,唯獨(dú)少了個(gè)類型,雖然說 File 沒有提供直接獲取類型的方法,但是我們可以通過獲取文件的全名,然后通過裁剪獲取到文件的后綴,便可獲取到文件的類型:
轉(zhuǎn)手一操作,自給自足也能獲取文件類型,真是個(gè)小機(jī)靈鬼~
以上我們都是基于文件目錄存在的情況下操作的,那么如果我們想要操作的文件目錄不存在?;蛘哂捎谖覀兊拇中膶⑽募夸浢Q輸入錯(cuò)了,那么將會(huì)發(fā)生什么情況,操作進(jìn)程是否能夠正常進(jìn)行?
結(jié)果便是拋出異常了,的確拋出異常才是正常的現(xiàn)象,針對(duì)一個(gè)不存在的文件目錄進(jìn)行操作豈不是瞎胡鬧
因此在我們不確定文件目錄是否存在的情況下我們可以這樣操作:
在圖中我們可以看到兩個(gè)我們沒見過的API方法,分別是 exists() 和 mkdirs().
exists():用于驗(yàn)證文件目錄是否存在
mkdirs():用于創(chuàng)建目錄
通過以上先驗(yàn)證后操作,我們成功避免了異常。這里需要了解的是,除了 mkdirs() 可以創(chuàng)建目錄之外,還有一個(gè) mkdir() 也是可以創(chuàng)建目錄的,這兩個(gè)方法除了少了一個(gè) s 之外,還有其他區(qū)別呢?
mkdir(): 只能創(chuàng)建一層目錄
mkdirs(): 可以創(chuàng)建多層目錄
我們目前的場(chǎng)景是 Test 目錄不存在,dir01 這個(gè)目錄自然也不存在,那么這個(gè)時(shí)候就得創(chuàng)建兩層目錄。但是我們使用 mkdir() 這個(gè)方法是行不通的,它無法創(chuàng)建。因此遇到這種情況我們應(yīng)當(dāng)使用 mkdirs()這個(gè)方法。
File類型
File 可以是一個(gè)文件也可以是一個(gè)文件集,文件集中可包含一個(gè)文件或者是一個(gè)文件夾,如果我們想要針對(duì)一個(gè)文件做讀寫操作,卻無意對(duì)一個(gè)文件夾進(jìn)行了操作,那就尷尬了,因此我們可以借助 isDirectory來判斷是否是文件夾:
輸入與輸出
上面我們談到 File 類的基本操作,接下來我們便進(jìn)入了I/O模塊。
輸入和輸出我們經(jīng)常使用 流 這個(gè)概念,如輸入流和輸出流。這是個(gè)抽象的概念,代表任何與能力產(chǎn)出數(shù)據(jù)的數(shù)據(jù)源對(duì)象或是有能力接受數(shù)據(jù)的接收端對(duì)象。流 屏蔽了實(shí)際 I/O 設(shè)備找那個(gè)處理數(shù)據(jù)的細(xì)節(jié)!
I/O 可以分為 輸入 和 輸出 兩部分。
輸入流中又分為 字節(jié)輸入流(InputStream) 和 字符輸入流(Reader),任何由 InputStream 或 Reader 派生而來的類都實(shí)現(xiàn)了 read() 這個(gè)方法,用來讀取單個(gè)字節(jié)或字節(jié)數(shù)組。
輸出流中又分為 字節(jié)輸出流(OutputStream) 和 字符輸出流(Writer),任何由 OutputStream 或 Writer 派生而來的類都實(shí)現(xiàn)了 write() 這個(gè)方法,用來寫入單個(gè)字節(jié)或字節(jié)數(shù)組。
因此我們可以看出 Java 中的規(guī)定:與輸入有關(guān)的所有類都應(yīng)該從 InputStream 繼承,與輸出有關(guān)的所有類都應(yīng)該從 OutputStream 繼承
InputStream
用來表示那些從不同數(shù)據(jù)源產(chǎn)生輸入的類
那些不同數(shù)據(jù)源具體又是哪些?常見的有:1. 字節(jié)數(shù)組 2. String 對(duì)象 3. 文件 4. “管道”(一端輸入,一端輸出)
其中每一種數(shù)據(jù)源都有對(duì)應(yīng)的 InputStream 子類可以操作:
類 | 功能 |
---|---|
ByteArrayInputStream | 允許將內(nèi)存的緩沖區(qū)當(dāng)作 InputStream 使用 |
StringBufferInputStream | 已廢棄,將String轉(zhuǎn)換成 InputStream |
FileInputStream | 用于從文件中讀取信息 |
PipedInputStream | 產(chǎn)生用于寫入相關(guān) PipedOutPutStream的數(shù)據(jù),實(shí)現(xiàn) 管道化 的概念 |
SequenceInputStream | 將兩個(gè)或多個(gè) InputStream 對(duì)象轉(zhuǎn)換成一個(gè) InputStream |
FilterInputStream | 抽象類,作為裝飾器 的接口,為其他InputStream 提供有用的功能 |
OutPutStream
該類別的類決定了輸出所要去往的目標(biāo):1. 字節(jié)數(shù)組 2. 文件 3. 管道
常見的 OutPutStream 子類有:
類 | 功能 |
---|---|
ByteArrayOutputStream | 在內(nèi)存中創(chuàng)建緩沖區(qū),所有送往 “流” 的數(shù)據(jù)都要放置在此緩沖區(qū) |
FileOutputStream | 用于將信息寫入文件 |
PipedOutputStream | 任何寫入其中的信息都會(huì)自動(dòng)作為相關(guān) PipedInputStream 的輸出,實(shí)現(xiàn) 管道化 的概念 |
FilterOutputStream | 抽象類,作為裝飾器 的接口,為其他 OutputStream 提供有用的功能 |
裝飾器
我們通過以上的認(rèn)識(shí),都看到了不管是輸入流還是輸出流,其中都有一個(gè)抽象類FilterInputStream 和 FilterOutputStream,這些類相當(dāng)于是一個(gè)裝飾器。在Java 中I/O 操作需要多種不同的功能組合,而這個(gè)便是使用裝飾器模式的理由所在。
何為裝飾器?裝飾器必須具有和它所裝飾對(duì)象的相同接口,但它也可以擴(kuò)展接口,它可以給我們提供了相當(dāng)多的靈活性,但它也會(huì)增加代碼的復(fù)雜性。
FilterInputStream 和FilterOutputStream 是用來提供裝飾器類接口以控制特定輸入流(InputStream)和輸出流(OutputStream)的兩個(gè)類。
FilterInputStream
InputStream 作為字節(jié)輸入流,那么讀取的數(shù)據(jù)理應(yīng)用字節(jié)數(shù)組接收,如下:
我們得借助一個(gè) byte 數(shù)組來接收讀取到值,然后轉(zhuǎn)為字符串類型。
既然我們有了裝飾器FilterInputStream ,那是否可以借助裝飾器的子類來幫我們實(shí)現(xiàn)讀操作呢?我們先來看下常用的FilterInputStream子類有哪些:
類 | 功能 |
---|---|
DataInputStream | 與 DataOutputStream 搭配使用,我們可以按照可移植方式從流讀取基本數(shù)據(jù)類型(int,char,long) |
BufferedInputStream | 使用它可以防止每次讀取時(shí)都得進(jìn)行實(shí)際寫操作。代表"緩沖區(qū)" |
其中DataInputStream允許我們讀取不同的基本數(shù)據(jù)類型數(shù)據(jù)以及String對(duì)象,搭配相應(yīng)的DataOutputStream,我們就可以通過數(shù)據(jù)"流" 將基本類型的數(shù)據(jù)從一個(gè)地方遷移到另一個(gè)地方。
然后說到BufferedInputStream 之前我們先看一組測(cè)試代碼:
現(xiàn)有三個(gè)文本文件,其中test01.txt 大小約為 610M,test02/test03均為空文本文件
那我們現(xiàn)在分別用普通的 InputStream + OutputStream 和裝飾后的BufferedInputStream + BufferedOutputStream 寫入文本
普通組合:
緩沖區(qū)組合:
可以看出兩種方式的分別耗時(shí),4864 ms 和 1275 ms。使用普通組合相當(dāng)于是緩沖區(qū)的 4 倍之久,如果文件更大的話,這個(gè)差異可是驚人的!驚訝的同時(shí)肯定也有所詫異,這是為什么呢?
如果用read()方法讀取一個(gè)文件,每讀取一個(gè)字節(jié)就要訪問一次硬盤,這種讀取的方式效率是很低的。即便使用read(byte b[])方法一次讀取多個(gè)字節(jié),當(dāng)讀取的文件較大時(shí),也會(huì)頻繁的對(duì)磁盤操作。
而BufferedInputStream的API文檔解釋為:在創(chuàng)建BufferedInputStream時(shí),會(huì)創(chuàng)建一個(gè)內(nèi)部緩沖區(qū)數(shù)組。在讀取流中的字節(jié)時(shí),可根據(jù)需要從包含的輸入流再次填充該內(nèi)部緩沖區(qū),一次填充多個(gè)字節(jié)。也就是說,Buffered類初始化時(shí)會(huì)創(chuàng)建一個(gè)較大的byte數(shù)組,一次性從底層輸入流中讀取多個(gè)字節(jié)來填充byte數(shù)組,當(dāng)程序讀取一個(gè)或多個(gè)字節(jié)時(shí),可直接從byte數(shù)組中獲取,當(dāng)內(nèi)存中的byte讀取完后,會(huì)再次用底層輸入流填充緩沖區(qū)數(shù)組。因此這種從直接內(nèi)存中讀取數(shù)據(jù)的方式要比每次都訪問磁盤的效率高很多。
BufferedInputStream/BufferedOutputStream不直接操作數(shù)據(jù)源,而是對(duì)其他字節(jié)流進(jìn)行包裝,它們是 處理流。
程序把數(shù)據(jù)保存到 BufferedOutputStream 緩沖區(qū)中,并沒有立即保存到文件里,緩沖區(qū)中的數(shù)組在以下情況會(huì)保存到文件中:
緩沖區(qū)已滿
flush() 清空緩沖區(qū)
close() 關(guān)閉流
FilterOutputStream
OutputStream 的基本操作如下:
通過調(diào)用write() 方法便可將值寫入文件中,這里有兩點(diǎn)需要注意:
寫入文檔默認(rèn)是覆蓋的方式
按我們理解調(diào)用兩次該方法,文本文件中的內(nèi)容應(yīng)該是兩行 公眾號(hào):小菜良記,但是實(shí)際上只用一行,這是因?yàn)楹竺鎸懭氲膬?nèi)容會(huì)覆蓋前面已經(jīng)存在的內(nèi)容,解決方法便是在構(gòu)造函數(shù)的時(shí)候加上append = true
寫入與讀取的區(qū)別在于,讀取的時(shí)候如果文件不存在會(huì)報(bào)錯(cuò),但是寫入的時(shí)候如果文件不存在,會(huì)默認(rèn)幫你創(chuàng)建文件
OutputStream中同樣存在裝飾器類FilterOutputStream,以下便是裝飾器類的常用子類:
類 | 功能 |
---|---|
DataOutputStream | 與DATAInputStream搭配使用,可以按照可移植方式向流中寫入基本類型數(shù)據(jù)(int,char,long等) |
BufferedOutputStream | 使用它避免每次發(fā)送數(shù)據(jù)時(shí)都要進(jìn)行實(shí)際的寫操作,代表 使用緩沖區(qū),可以調(diào)用flush 清空緩沖區(qū) |
DataOutputStream 和 BufferedOutputStream 在上面已經(jīng)講到,這里就不再贅述。
Reader 與 Writer
在 Java 1.1 的時(shí)候,對(duì)基本的I/O流類庫進(jìn)行了重大的修改,增添了 Reader 和 Writer 兩個(gè)類。在我之前局限的認(rèn)知中,會(huì)誤以為這兩個(gè)類的出現(xiàn)是為了替代 InputStream 和 OutputStream ,但事實(shí)也并非與我局限認(rèn)知所似。
InputStream 和 OutputStream 是以面向字節(jié)的形式為 I/O 提供功能,而 Reader 和 Writer是提供兼容 Unicode于面向字符的形式為 I/O 提供功能
這兩者共存,并提供了適配器 - InputStreamReader 和 OutputStreamWriter
InputStreamReader 可以把 InputStream 轉(zhuǎn)換為 Reader
OutputStreamWriter 可以把 OutputStream 轉(zhuǎn)換為 Writer
這兩者雖然不能說完全相同,但也是極為相似,對(duì)照如下:
字節(jié)流 | 字符流 |
---|---|
InputStream | Reader |
OutputStream | Writer |
FileInputStream | FileReader |
FileOutputStream | FileWriter |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
PipedInputStream | PipedReader |
PipedOutputStream | PipedWriter |
甚至裝飾者類都幾乎相似:
字節(jié)流 | 字符流 |
---|---|
FilterInputStream | FilterReader |
FilterOutputStream | FilterWriter |
BufferedInputStream | BufferedReader |
BufferedOutputStream | BufferedWriter |
PrintStream | PrintWriter |
使用Reader 和 Writer 的方式也十分簡單:
我們順便看下裝飾器的使用BufferedReader 與 BufferedWriter
RandomAccessFile
RandomAccessFile 適用于由大小已知的記錄組成的文件,所以我們可以使用 seek() 將記錄從一處轉(zhuǎn)移到另一處,然后讀取或者修改記錄。文件中記錄的大小不一定都相同,只要我們能夠確定哪些記錄有多大以及它們?cè)谖募械奈恢眉纯伞?/p>
我們從圖中可以看到 RandomAccessFile 并非繼承于 InputStream 和 OutputStream 兩個(gè)接口,而是繼承于有些陌生的DateInput 和 DataOutput。
真是個(gè)有點(diǎn)特立獨(dú)行的類~我們繼續(xù)來看下它的構(gòu)造函數(shù):
我們這邊只截取了構(gòu)造函數(shù)的一部分,畢竟只截重點(diǎn)就行~
觀察構(gòu)造器可以發(fā)現(xiàn),這里定義了四種模式:
r | 以只讀的方式打開文本,也就意味著不能用write來操作文件 |
---|---|
rw | 讀操作和寫操作都是允許的 |
rws | 每當(dāng)進(jìn)行寫操作,同步的刷新到磁盤,刷新內(nèi)容和元數(shù)據(jù) |
rwd | 每當(dāng)進(jìn)行寫操作,同步的刷新到磁盤,刷新內(nèi)容 |
這有什么用呢?說白了就是 RandomAccessFile 這個(gè)類什么都要。既能讀,又能寫
從本質(zhì)上來說,RandomAccessFile 的工作方式類似于把 DataInputStream 和 DataOutputStream 組合起來使用,還添加了一些方法,其中方法getFilePointer() 用于查找當(dāng)前所處的文件位置,seek()用于在文件內(nèi)移至新的位置,length() 用于判斷文件的最大尺寸。第二個(gè)參數(shù)用于表明我們是 "隨機(jī)讀(r)" 還是 "既讀又寫(rw)",但它不支持單獨(dú) 寫文件。我們實(shí)際來操作一下:
獲取只讀RandomAccessFile:
獲取可讀可寫RandomAccessFile
我們首先從向文件中寫入了test 四個(gè)單詞,然后將頭指針移動(dòng)3位后繼續(xù)寫入File四個(gè)單詞,結(jié)果就變成了testFile,這是因?yàn)橐苿?dòng)指針后是以第四個(gè)位置開始寫入。
ZIP
看到zip這個(gè)詞,我們理所應(yīng)當(dāng)?shù)木蜁?huì)想到壓縮文件,沒錯(cuò)壓縮文件在 Java I/O中也是極其重要的存在。也許更應(yīng)該說對(duì)文件的壓縮在我們的開發(fā)中也是極其重要的存在。
在 Java 內(nèi)置類中提供了需要關(guān)于ZIP 壓縮的類,可以使用 java.util.zip 包中的ZipOutuputStream 和 ZipInputStream 來實(shí)現(xiàn)文件的 壓縮 和 解壓縮。我們先來看下如何對(duì)文件進(jìn)行壓縮~
ZipOutputStream
ZipOutputStream 的構(gòu)造方法如下:
public ZipOutputStream(OutputStream out) {/* doSomething */}
我們需要傳入一個(gè) OutputStream 對(duì)象。因此我們也大致可以認(rèn)為 壓縮文件 相當(dāng)于是向一個(gè) 壓縮文件中寫入數(shù)據(jù),聽起來可能會(huì)有點(diǎn)繞。我們先看下ZipOutputStream中有哪些API:
方法 | 返回值 | 說明 |
---|---|---|
putNextEntry(ZipEntry e) | void | 開始寫一個(gè)新的 ZipEntry,并將流內(nèi)的位置移至此 entry 所值數(shù)據(jù)的開頭 |
write(byte[] b, int off, int len) | void | 將字節(jié)數(shù)組寫入當(dāng)前 ZIP 條目數(shù)據(jù) |
setComment(String command) | void | 設(shè)置此 ZIP 文件的注釋文字 |
finish() | void | 完成寫入ZIP 輸出流的內(nèi)容,無須關(guān)閉它所配合的 OutputStream |
我們來演示一下如何壓縮文件:
場(chǎng)景:我們需要將D盤目錄下的 TestFile文件夾壓縮到 D盤下的 test.zip 中
具體的操作邏輯如下:
通過以上步驟我們便可以很順利的將一個(gè)文件壓縮
ZipInputStream
說完如何將文件壓縮,那自然要會(huì)如何將文件解壓縮!
public ZipInputStream(InputStream in) {/* doSomethings */}
ZipInputStream 與壓縮流類似,構(gòu)造函數(shù)同樣需要傳入一個(gè) InputStream 對(duì)象,毋庸置疑,API肯定也是一一對(duì)應(yīng)的:
方法 | 返回值 | 說明 |
---|---|---|
read(byte[] b, int off, int len) | int | 讀取目標(biāo) b 數(shù)組內(nèi) off 偏移量的位置,長度是 len 字節(jié) |
avaiable() | int | 判斷是否已讀完目前 entry 所指定的數(shù)據(jù),已讀完返回 0,否則返回 1 |
closeEntry() | void | 關(guān)閉當(dāng)前 ZIP 條目并定位流以讀取下一個(gè)條目 |
skip(long n) | long | 跳過當(dāng)前 ZIP 條目中指定的字節(jié)數(shù) |
getNextEntry() | ZipEntry | 讀取下一個(gè)ZipEntry,并將流內(nèi)的位置移至該 entry 所指數(shù)據(jù)的開頭 |
createZipEntry(String name) | ZipEntry | 以指定的name參數(shù)新建一個(gè)ZipEntry對(duì)象 |
那下面我們便動(dòng)手操作一下如何解壓一個(gè)文件:
不必被代碼長度嚇到,認(rèn)真閱讀便會(huì)發(fā)現(xiàn)解壓文件也很簡單:
我們通過 getNextEntry() 方法來獲取到一個(gè)ZipEntry,這里取到文件方式類似于深度遍歷,每次返回的目錄大致如下:
每次都會(huì)遍歷完一個(gè)目錄下的所有文件,例如 dir01 文件夾下的所有文件,才會(huì)繼續(xù)遍歷 dir02 文件夾,所以我們不必使用遞歸的方式去獲取所有文件。取到每一個(gè)文件后,通過 ZipFile獲取輸出流,然后寫入到解壓后的文件中。大致流程如下:
新 I/O
JDK1.4的java.nio.* 包中引入了新的 JavaI/O 類庫,其目的也簡單,就是提高速度。實(shí)際上,舊的I/O包已經(jīng)使用 nio 重新實(shí)現(xiàn)過,以便充分利用這種速度提高。
只要使用的結(jié)構(gòu)更接近于操作系統(tǒng)執(zhí)行I/O的方式,那么速度自然也會(huì)提高,因此就產(chǎn)生了兩個(gè)概念:通道 和 緩沖器。
我們?cè)撛趺蠢斫?通道 和 緩沖器 兩個(gè)概念呢。我們可以認(rèn)為緩沖器相當(dāng)于是一輛煤礦中的小火車,通道相當(dāng)于火車的軌道,小火車載著滿滿的煤礦從礦源運(yùn)往它處。因此我們并沒有直接和通道交互,而是和緩沖器交互,并把緩沖器派送到通道。通道要么從緩沖器獲得數(shù)據(jù),要么向緩沖器發(fā)送數(shù)據(jù)。
ByteBuffer是唯一直接與通道直接交互的緩沖器,可以存儲(chǔ)未加工字節(jié)的緩沖器。
ByteBuffer buffer = ByteBuffer.allocate(1024);
ByteBuffer 的創(chuàng)建方式通常可以通過allocate()方法來指定大小創(chuàng)建。同時(shí)ByteBuffer中支持 4中創(chuàng)建 ByteBuffer
為了更好支持 新I/O ,舊 I/O 類庫中有三個(gè)類被修改了,用以產(chǎn)生FileChannel。這個(gè)被修改的類分別的:FileInputStream,F(xiàn)ileOutputStream以及用于讀寫兼?zhèn)涞? RandomAccessFile。這里值得注意的是這些都是字節(jié)操作流,因?yàn)樽址鞑荒苡糜诋a(chǎn)生通道,但是 Channels 中提供了實(shí)用的方法,用于在通道中產(chǎn)生 Reader 和 Writer
獲取通道
我們?cè)谏厦嬉呀?jīng)了解到了有三個(gè)類支持產(chǎn)生通道,具體產(chǎn)生通道的方法如下:
以上便是創(chuàng)建通道的三種方式,并且進(jìn)行了讀寫操作的測(cè)試。我們看一下圖中的測(cè)試代碼,然后總結(jié)一下:
getChannel()方法將會(huì)產(chǎn)生一個(gè) FileChannel。我們可以向它傳送可用于讀寫的ByteBuffer。我們將字節(jié)存放于 ByteBuffer 的方法之一是:使用 put()方法直接對(duì)它們進(jìn)行填充,填入一個(gè)或多個(gè)字節(jié),或基本數(shù)據(jù)類型的值。不過,也可以使用 wray()方法將已存在的字節(jié)數(shù)組 "包裝" 到 ByteBuffer 中。這樣子就可以不用在復(fù)制底層的數(shù)組,而是把它作為所產(chǎn)生的 ByteBuffer 的存儲(chǔ)器,可以稱之為 數(shù)組支持的ByteBuffer
我們還可以看到 FileChannel 使用到的 position() 方法,這個(gè)方法可以在文件內(nèi)隨處移動(dòng)FileChannel,在這里,我們把它移動(dòng)到最后,然后進(jìn)行其他的讀寫操作。
對(duì)于只讀訪問,我們必須顯式地使用靜態(tài)的allocate() 方法來分配 ByteBuffer。如果我們想要獲取更好的速度我們也可以使用 allocateDirect() ,以產(chǎn)生一個(gè)與操作系統(tǒng)有更高耦合性的 "直接" 緩沖器。但是這種分配的開支會(huì)更大,并且具體實(shí)現(xiàn)也隨操作系統(tǒng)的不同而不同。
如果我們想要的調(diào)用 read() 來向ByteBuffer 存儲(chǔ)字節(jié),就必須調(diào)用緩沖器上的flip() 方法,這是用來告知 FileChannel ,讓它準(zhǔn)備好讓別人讀取字節(jié)的準(zhǔn)備,當(dāng)然,這也是為了獲取最大速度。這里我們用 ByteBuffer 來接收字節(jié)后就沒有繼續(xù)使用緩沖器來進(jìn)一步操作,如果需要繼續(xù)read() 的話,我們就必須得調(diào)用 clear() 方法來為每個(gè) read() 方法做準(zhǔn)備。
通道相連
程序員往往都是比較懶惰的,上面那種讀取后再通知 FileChannel 的方式似乎有些麻煩。那么有沒有更加簡單的方法?肯定是有的,不然我也不會(huì)問是吧~ 那就是讓一個(gè)通道與另外一個(gè)通道直接相連接,這就得借助特殊的方法transferTo() 和 transferFrom() 。具體使用如下:
借助方法1 或 方法2 都可以成功將文件寫入到 test03.txt 文件中
到此,相信大家對(duì)“如何使用Java IO”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。