這篇文章主要講解了“java的文件IO舉例分析”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“java的文件IO舉例分析”吧!
成都創(chuàng)新互聯(lián)公司專(zhuān)注于同心企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城系統(tǒng)網(wǎng)站開(kāi)發(fā)。同心網(wǎng)站建設(shè)公司,為同心等地區(qū)提供建站服務(wù)。全流程按需定制,專(zhuān)業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專(zhuān)業(yè)和態(tài)度為您提供的服務(wù)
JDK 1.4的java.nio.*包中引入了新的IO類(lèi)庫(kù),其目的在于提高速度。實(shí)際上,舊的IO包已經(jīng)使用nio重新實(shí)現(xiàn)過(guò),以便充分利用這種速度提高。速度提高源自于所使用的結(jié)構(gòu)更接近于操作系統(tǒng)執(zhí)行IO的方式:通道和緩沖器。我們可以把它想象成一個(gè)煤礦,通道是一個(gè)包含煤層(數(shù)據(jù))的礦藏,而緩沖器則是派送到礦藏的卡車(chē)??ㄜ?chē)載滿(mǎn)煤炭而歸,我們?cè)購(gòu)目ㄜ?chē)上獲得煤炭。也就是說(shuō),我們并沒(méi)有直接和通道交互,我們只是和緩沖器交互,并把緩沖器派送到通道。要么從緩沖器獲得數(shù)據(jù),要么向緩沖器發(fā)送數(shù)據(jù)。
唯一直接與通道交互的緩沖器是ByteBuffer——可以存儲(chǔ)未加工字節(jié)的緩沖器。java.nio.ByteBuffer是相當(dāng)基礎(chǔ)的類(lèi):通過(guò)告知分配多少存儲(chǔ)空間來(lái)創(chuàng)建一個(gè)ByteBuffer對(duì)象,并且還有一個(gè)方法選擇集,用于以原始的字節(jié)形式或基本數(shù)據(jù)類(lèi)型輸出和讀取數(shù)據(jù)。但是,沒(méi)辦法輸出或讀取對(duì)象,即使是字符串對(duì)象也不行。這種處理雖然很低級(jí),但卻正好,因?yàn)檫@是大多數(shù)操作系統(tǒng)中更有效的映射方式。
舊IO類(lèi)庫(kù)有三個(gè)類(lèi)被修改了,用以產(chǎn)生FileChannel。這三個(gè)被修改類(lèi)是FileInputStream、FileOutputStream以及用于既讀又寫(xiě)的RandomAccessFile。這些都是字節(jié)操作流,與底層nio性質(zhì)一致。Reader和Writer這些字符模式類(lèi)不能用于產(chǎn)生通道;但是java.nio.channels.Channels類(lèi)提供了適用方法,用于在通道中產(chǎn)生Reader和Writer。
//演示上面三種類(lèi)型的流,用于產(chǎn)生可寫(xiě)的,可讀可寫(xiě)的及可讀的通道 package com.wise.tiger; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class GetChannel { private static final int BUFF_SIZE = 1024; public static void main(String[] args) throws IOException { // 寫(xiě)文件 FileChannel fc = new FileOutputStream("data.txt").getChannel(); /* * 使用warp()方法將已存在的字節(jié)數(shù)組包裝到ByteBuffer中 也可以使用put方法直接進(jìn)行填充 */ fc.write(ByteBuffer.wrap("I'm Peppa Pig.".getBytes())); fc.close(); // 文件末尾添加內(nèi)容 fc = new RandomAccessFile("data.txt", "rw").getChannel(); fc.position(fc.size());// 移動(dòng)到文件末尾 fc.write(ByteBuffer.wrap("This is my little brother, George".getBytes())); fc.close(); // 讀文件 fc = new FileInputStream("data.txt").getChannel(); /* * 分配ByteBuffer,對(duì)于只讀訪(fǎng)問(wèn),必須顯式地使用靜態(tài)的allocate()方法來(lái)分配ByteBuffer * nio的目標(biāo)就是快速移動(dòng)大量數(shù)據(jù),因此ByteBuffer的大小就顯得尤為重要(必須通過(guò)實(shí)際運(yùn)行程序找到最佳尺寸) */ ByteBuffer buff = ByteBuffer.allocate(BUFF_SIZE); /* * 一旦調(diào)用read()來(lái)告知FileChannel向ByteBuffer存儲(chǔ)字節(jié),就必須調(diào)用緩沖器上的flip(),讓它做好讓別人讀取字節(jié)的準(zhǔn)備。 * 如果打算使用緩沖器執(zhí)行進(jìn)一步read()操作,也必須得使用clear()來(lái)為每個(gè)read()做好準(zhǔn)備,如下copy文件 */ fc.read(buff); buff.flip(); while (buff.hasRemaining()) { System.out.print((char) buff.get()); } fc.close(); } }
這里展示的任何流類(lèi),getChannel()將會(huì)產(chǎn)生一個(gè)FileChanel。通道是一個(gè)相當(dāng)基礎(chǔ)的東西:可以向它傳送用于讀寫(xiě)的ByteBuffer,并且可以鎖定文件的某些區(qū)域用于獨(dú)占式訪(fǎng)問(wèn)。
public class ChannelCopy { private static final int CAPACITY = 1024; public static void main(String[] args) throws IOException { if(args.length != 2)System.exit(1); FileChannel in = new FileInputStream(args[0]).getChannel(), out = new FileOutputStream(args[1]).getChannel(); ByteBuffer buff = java.nio.ByteBuffer.allocate(CAPACITY); while(in.read(buff) != -1) { buff.flip(); out.write(buff); buff.clear(); } out.close(); in.close(); } }
然而,這個(gè)程序并不是處理此類(lèi)操作的理想方式,特殊方法transferTo()和transferFrom()則允許我們將一個(gè)通道和另一個(gè)通道直接相連
public class ChannelCopy { private static final int CAPACITY = 1024; public static void main(String[] args) throws IOException { if(args.length != 2)System.exit(1); FileChannel in = new FileInputStream(args[0]).getChannel(), out = new FileOutputStream(args[1]).getChannel(); in.transferTo(0,in.size(),out);//or out.transferFrom(in,0,in.size); out.close(); in.close(); } }
在GetChannel.java中,必須每次只讀取一個(gè)字節(jié)的數(shù)據(jù),然后將每個(gè)byte類(lèi)型強(qiáng)制轉(zhuǎn)換成char類(lèi)型。而java.nio.CharBuffer有一個(gè)toString方法:返回一個(gè)包含緩沖器中所有字符的字符串。ByteBuffer可以看做是具有asCharBuffer()方法的CharBuffer。
public class BufferToText { private static final int CAPACITY = 1024; public static void main(String[] args) { try{ var fc = new FileOutputStream("data.txt").getChannel(); fc.write(ByteBuffer.wrap("來(lái)來(lái),我是一個(gè)香蕉。。".getBytes())); fc.close(); fc = new FileInputStream("data.txt").getChannel(); var buff = ByteBuffer.allocate(CAPACITY); fc.read(buff); buff.flip(); System.out.println(buff.asCharBuffer()); buff.rewind();//返回到數(shù)據(jù)開(kāi)始部分 var encoding = System.getProperty("file.encoding");//獲取默認(rèn)字符集 System.out.println("使用" + encoding + "解碼結(jié)果: " + Charset.forName(encoding).decode(buff)); fc = new FileOutputStream("data.txt").getChannel(); fc.write(ByteBuffer.wrap("來(lái)來(lái),我是一個(gè)榴蓮".getBytes("UTF-8"))); fc.close(); fc = new FileInputStream("data.txt").getChannel(); buff.clear(); fc.read(buff); buff.flip(); System.out.println(buff.asCharBuffer()); fc = new FileOutputStream("data.txt").getChannel(); buff = ByteBuffer.allocate(24); buff.asCharBuffer().put("如花貌美容顏"); fc.write(buff); fc.close(); fc = new FileInputStream("data.txt").getChannel(); buff.clear(); fc.read(buff); buff.flip(); System.out.println(buff.asCharBuffer()); }catch (IOException e) { e.printStackTrace(); } } }
緩沖器容納的是普通的字節(jié),為了把它們轉(zhuǎn)換成字符,我們要么在輸入它們的時(shí)候?qū)ζ溥M(jìn)行編碼,要么在將其從緩沖器輸出時(shí)對(duì)他們進(jìn)行解碼(可以使用java.nio.charset.Charset類(lèi)實(shí)現(xiàn)這些功能).
盡管ByteBuffer只能保存字節(jié)類(lèi)型數(shù)據(jù),但是它可以從其所容納的字節(jié)中產(chǎn)生出各種不同的基本類(lèi)型值的方法
CharBuffer ====> asCharBuffer();
ShorBuffer ====> asShortBuffer();
IntBuffer ====> asIntBuffer();
LongBuffer ====> asLongBuffer();
FloatBuffer ====> asFloatBuffer();
DoubleBuffer ====> asDoubleBuffer();
這些Buffer覆蓋了你能通過(guò)IO發(fā)送的基本數(shù)據(jù)類(lèi)型:byte, short, int, long, float, double 和 char。
注意:使用shortBuffer的put()方法時(shí),需要進(jìn)行類(lèi)型轉(zhuǎn)換。
視圖緩沖器(view buffer)可以讓我們通過(guò)某個(gè)特定的基本數(shù)據(jù)類(lèi)型的視窗查看其底層的ByteBuffer。ByteBuffer依然是實(shí)際存儲(chǔ)數(shù)據(jù)的地方,“支持”著前面的視圖,因此對(duì)視圖的任何修改都會(huì)映射成為對(duì)ByteBuffer中數(shù)據(jù)的修改。
一旦底層的ByteBuffer通過(guò)視圖緩沖器填滿(mǎn)了整數(shù)或其他基本類(lèi)型時(shí),就可以直接被寫(xiě)到通道中。正像從通道中讀取那樣容易,然后使用視圖緩沖器可以把任何數(shù)據(jù)都轉(zhuǎn)化為某一特定的基本類(lèi)型。
下圖闡明了nio類(lèi)之間的關(guān)系,便于我們理解怎么移動(dòng)和轉(zhuǎn)換數(shù)據(jù)。如果想把一個(gè)字節(jié)數(shù)組寫(xiě)到文件中去,那么就應(yīng)該使用ByteBuffer.wrap()方法把字節(jié)數(shù)組包裝起來(lái),然后用getChannel()方法在FileOutputStream上打開(kāi)一個(gè)通道,接著將來(lái)自于ByteBuffer的數(shù)據(jù)寫(xiě)到FileChannel。
注意:BytBuffer是將數(shù)據(jù)移進(jìn)移出通道的唯一方式,并且只能創(chuàng)建一個(gè)獨(dú)立的基本類(lèi)型緩沖器,或者使用as方法從ByteBuffer中獲得。也就是說(shuō),不能把基本類(lèi)型的緩沖器轉(zhuǎn)換成ByteBuffer。然而,我們可以經(jīng)由視圖緩沖器將基本類(lèi)型數(shù)據(jù)移進(jìn)移出Bytebuffer,所以也就不是什么真正的限制了。
Buffer有數(shù)據(jù)和可以高效地訪(fǎng)問(wèn)及操作這些數(shù)據(jù)的四個(gè)索引組成,mark(標(biāo)記)、position(位置)、limit(界限)和capacity(容量)。下面是用于設(shè)置和復(fù)位索引以及查詢(xún)它們的值的方法。
capacity() | 返回緩沖區(qū)容量 |
clear() | 清空緩存區(qū),將position設(shè)置為0,limit設(shè)置為容量??梢哉{(diào)用此方法覆寫(xiě)緩沖區(qū) |
flip() | 將limit設(shè)置為position,position置位0.此方法用于準(zhǔn)備從緩沖區(qū)讀取已經(jīng)寫(xiě)入的數(shù)據(jù) |
limit() | 返回limit的值 |
limit(int lim) | 返回limit的值 |
mark() | 將mark設(shè)置為position |
position() | 返回position值 |
position(int pos) | 返回position值 |
remaining() | 返回(limit - position) |
hasRemaining() | 是否有介于position和limit之間的元素 |
在緩沖器中插入和提取數(shù)據(jù)的方法會(huì)更新這些索引,用于反應(yīng)所發(fā)生的變化。
內(nèi)存映射文件允許創(chuàng)建和修改因?yàn)樘蠖荒芊湃雰?nèi)存的文件。可以假定整個(gè)文件都放在內(nèi)存中,而且可以完全把它當(dāng)作非常大的數(shù)組訪(fǎng)問(wèn)。
public class LargeMappedFiles { static int length = 0x8FFFFFF; // 128 MB public static void main(String[] args) throws Exception { MappedByteBuffer out = new RandomAccessFile("test.dat", "rw").getChannel() .map(FileChannel.MapMode.READ_WRITE, 0, length); for (int i = 0; i < length; i++) { out.put((byte) 'x'); } print("Finished writing"); for (int i = length / 2; i < length / 2 + 6; i++) { printnb((char) out.get(i)); } } }
使用map()產(chǎn)生MappedByteBuffer,一個(gè)特殊類(lèi)型的直接緩沖器,注意:必須指定映射文件初始位置和映射區(qū)域長(zhǎng)度,意味著可以映射某個(gè)大文件的較小部分。
MappedByteBuffer由ByteBuffer繼承而來(lái),因此它具有ByteBuffer的所有方法。
前面程序創(chuàng)建的文件為128MB,這可能比操作系統(tǒng)所允許一次載入內(nèi)存的空間大。但似乎我們可以一次訪(fǎng)問(wèn)到整個(gè)文件。因?yàn)橹挥幸徊糠治募湃肓藘?nèi)存,其他被交換了出去。用這種方式,很大的文件(可達(dá)2GB)也可以很容易地修改。注意底層操作系統(tǒng)的文件映射工具是用來(lái)最大化地提高性能的。
映射文件的所有輸出必須使用RandomAccessFile。
JDK 1.4引入了文件加鎖機(jī)制,允許同步訪(fǎng)問(wèn)某個(gè)作為共享資源的文件。文件鎖對(duì)其他的操作系統(tǒng)進(jìn)程是可見(jiàn)的,因?yàn)镴ava的文件加鎖直接映射到本地操作系統(tǒng)的加鎖工具。
public class FileLocking { public static void main(String[] args) throws Exception { FileOutputStream fos= new FileOutputStream("file.txt"); FileLock fl = fos.getChannel().tryLock(); if(fl != null) { System.out.println("Locked File"); TimeUnit.MILLISECONDS.sleep(100); fl.release(); System.out.println("Released Lock"); } fos.close(); } } /* Locked File Released Lock */
通過(guò)對(duì)FileChannel調(diào)用tryLock()或lock(),就可以獲得整個(gè)文件的FileLock。(SocketChannel、DatagramChannel和ServerSocketChannel不需要加鎖,因?yàn)樗鼈兪菑膯芜M(jìn)程實(shí)體繼承而來(lái),通常不在兩個(gè)進(jìn)程之間共享socket。)tryLock()是非阻塞式的,它設(shè)法獲取鎖,如果不能得到(當(dāng)其他一些進(jìn)程已經(jīng)持有相同的鎖,并且不共享時(shí)),它將直接從方法調(diào)用返回。lock()是阻塞式的,它要阻塞進(jìn)程直至鎖可以獲得,或調(diào)用lock()的線(xiàn)程中斷,或調(diào)用lock()的通道關(guān)閉。使用FileLock.release()可以釋放鎖。
也可以使用如下方法對(duì)文件的一部分上鎖:
tryLock(long position, long size, boolean shared)
或者
lock(long position, long size, boolean shared)
其中加鎖區(qū)域由size-position決定,第三個(gè)參數(shù)指定是否共享鎖。
盡管無(wú)參的加鎖方法將根據(jù)文件尺寸變化而變化,但是具有固定尺寸的鎖不隨文件尺寸變化而變化。如果你獲得了某一區(qū)域(從position到position+size)上的鎖,當(dāng)文件增大超出position+size時(shí),那么在position+size之外的部分不會(huì)被鎖定。無(wú)參數(shù)的加鎖方法會(huì)對(duì)整個(gè)文件進(jìn)行加鎖,甚至文件變大后也是如此。
對(duì)于獨(dú)占鎖或者共享鎖的支持必須有底層的操作系統(tǒng)提供。如操作系統(tǒng)不支持共享鎖并未每一個(gè)請(qǐng)求都創(chuàng)建鎖,那么它就會(huì)使用獨(dú)占鎖。鎖的類(lèi)型可以通過(guò)FileLock.isShared()進(jìn)行查詢(xún)。
對(duì)映射文件的部分加鎖
文件映射通常應(yīng)用于極大的文件。我們可能需要對(duì)這種巨大的文件進(jìn)行部分加鎖,以便其他進(jìn)程可以修改文件中未被加鎖的部分。例如,數(shù)據(jù)庫(kù)就是這樣,因此多個(gè)用戶(hù)可以同時(shí)訪(fǎng)問(wèn)到它。
public class LockingMappedFIles { static final int LENGTH = 0x8FFFFFF;//128M static FileChannel fc; public static void main(String[] args) throws IOException { fc = new RandomAccessFile("test.dat", "rw").getChannel(); MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH); for(int i = 0; i < LENGTH; i++)out.put((byte)'x'); new LockAndModify(out, 0, 0 + LENGTH / 3); new LockAndModify(out, LENGTH / 2, LENGTH / 2 + LENGTH / 4); } private static class LockAndModify extends Thread{ private ByteBuffer buff; private int start,end; public LockAndModify(ByteBuffer buff, int start, int end) { this.start = start; this.end = end; buff.limit(end); buff.position(start); this.buff = buff.slice(); } @Override public void run() { try { FileLock lock = fc.lock(start,end,false); System.out.println("Locked:" + start + "to" + end); while(buff.position() < buff.limit() - 1) buff.put((byte)(buff.get() + 1)); lock.release(); System.out.println("Released:" + start + "to" + end); } catch (IOException e) { e.printStackTrace(); } } } }
線(xiàn)程類(lèi)LockAndModify創(chuàng)建了緩沖區(qū)和利于修改的slice(),然后再run中,獲得文件通道上的鎖(不能獲得緩沖器上的鎖,只能獲得通道上的)。lock()類(lèi)似于獲得一個(gè)對(duì)象的線(xiàn)程鎖----現(xiàn)在處在臨界區(qū),即對(duì)該部分文件具有獨(dú)占訪(fǎng)問(wèn)權(quán)。
如果有java虛擬機(jī),它會(huì)自動(dòng)釋放鎖,或者關(guān)閉加鎖的通道。不過(guò)也可以像程序中那樣,顯式地為FileLock對(duì)象調(diào)用release()來(lái)釋放。
感謝各位的閱讀,以上就是“java的文件IO舉例分析”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)java的文件IO舉例分析這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
本文題目:java的文件IO舉例分析
轉(zhuǎn)載注明:http://bm7419.com/article2/jcsdoc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開(kāi)發(fā)、手機(jī)網(wǎng)站建設(shè)、服務(wù)器托管、虛擬主機(jī)、云服務(wù)器、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)