java的文件IO舉例分析

這篇文章主要講解了“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ù)

文件I/O

        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();  
    }  
}

轉(zhuǎn)換數(shù)據(jù)

     在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)這些功能).  

獲取基本類(lèi)型

      盡管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)型。

用緩沖器操作數(shù)據(jù)

      下圖闡明了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,所以也就不是什么真正的限制了。

java的文件IO舉例分析
 

緩沖器細(xì)節(jié)

      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)存映射文件

      內(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)

成都seo排名網(wǎng)站優(yōu)化