Java基礎(5)-----IO_2_NIO

思維導圖

一.NIO的目的

    NIO存在的主要目的在於提高速度,並且,IO類也被NIO重新實現了,速度也有一定的提高。

二.NIO的原理

2.1運行模型

        在下圖中可以看到,數據源相較於IO類沒有多大的變化,通道類Channel則是通過IO類的FlieInputStream,FileOutputStream,RandomAccessFile類取得,而內存會利用一個ByteBuffer和通道類進行交互。打個比方來說,數據源相當於是一座礦山,Channel類相當於是通過上述三個IO類所打的隧道,ByteBuffer相當於車輛,往來於工地(內存)和隧道之間,進行貨物(數據)的輸送。

 在這幅圖中, 數據源和Channel類由FlieInputStream,FileOutputStream,RandomAccessFile類的getChannel()方法取得。

貨車ByteBuffer的核心是一個數組,用來存儲數據,還有一些字段,用來對貨車的數據進行一些操作。

ByteBuffer主要有以下幾個字段:

  1. position:當前數組指針所在的地方
  2. capacity:容量,即數組的大小限制
  3. limit:限制,他是在容量這一限制下的又一個限制,即position最後可以達到的位置
  4. mark:一個標記,標記了當時position的值,在重置時有用。

那麼貨車一般會有什麼操作呢:

  • put:放入數據

  • flip:當數據已經輸入完成後,需要讀取數據時,使用flip(),他會將limit設置在數據區最後的位置,標誌數據已被讀取完了。而position會被設置在數組的開頭,表示開始讀取數據。
  • get:從buffer裏讀一個字節,並把postion移動一位。上限是limit,即寫入數據的最後位置

  • clear:將position置爲0,並不清除buffer內容。 
  • mark: 將mark值賦爲position的值。
  • reset:將此緩衝區的位置重置爲以前標記的位置,調用此方法不更改也不丟棄標記的值。

2.2簡單使用

//定義輸入的數據源
        File file = new File("e:/in.txt");
        try {
            //通過輸入流獲得隧道
            FileChannel fc = new FileInputStream(file).getChannel();
            //定義一個ByteBuffer貨車出來,必須給貨車分配容量,才能更快。
            ByteBuffer bb = ByteBuffer.allocate(1024);
            //隧道指定貨車讀取數據
            fc.read(bb);
            //準備讀取貨車中的數據
            bb.flip();
            //輸出到控制檯
            while(bb.hasRemaining()){
                System.out.println((char)bb.get());
            }

            //寫入到另一份文件中
            File outFile = new File("e:/out.txt");
            FileChannel fc2 = new FileOutputStream(outFile).getChannel();
            //由於剛纔讀取過貨車中的信息,所以現在positon位於limit上,即數據已經讀取完了
            // 所以重置以下,方便讀取
            bb.flip();
            fc2.write(bb);
            //也可以使用其他的方法寫入數據wrap方法會指定新的ByteBuffer貨車,進行運輸
            //方法中的Byte數組會直接打包寫入
            fc2.write(ByteBuffer.wrap("new way".getBytes()));

            fc.close();
            fc2.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }

 

 三.常用功能

3.1字符輸出

在上面的程序中,如果輸入數據源中有中文,那麼輸出到控制檯就會出現亂碼,所以,我們可以在輸入數據時進行編碼,或者在輸出數據時解碼。

//定義輸入的數據源
        File file = new File("e:/in.txt");
        try {
            //通過輸入流獲得隧道
            FileChannel fc = new FileInputStream(file).getChannel();
            //定義一個ByteBuffer貨車出來,必須給貨車分配容量,才能更快。
            ByteBuffer bb = ByteBuffer.allocate(1024);
            //隧道指定貨車讀取數據
            fc.read(bb);
            bb.flip();
            //準備讀取貨車中的數據
            Charset charset = Charset.forName("GBK");
            //進行解碼
            System.out.println(charset.decode(bb));

            //寫入到另一份文件中
            File outFile = new File("e:/out.txt");
            FileChannel fc2 = new FileOutputStream(outFile).getChannel();
            //由於剛纔讀取過貨車中的信息,所以現在positon位於limit上,即數據已經讀取完了所以重置以下,方便讀取
            bb.flip();
            //輸入時不編碼也可以將中文輸入,當然輸入編碼,輸出就可以不解碼了
            fc2.write(bb);
            //輸入字符串則可以採用這種編碼方式
            fc2.write(ByteBuffer.wrap("小狗".getBytes("GBK")));

            fc.close();
            fc2.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }

3.2格式化:視圖

在NIO包中,除了ByteBuffer外,還有IntBuffer,FloatBuffer等一些列的基本類型的Buffer,利用他們是ByteBuffer的視圖,依舊是說,實際上的處理還是ByteBuffer,我們可以利用這些Buffer對基本類型的數據進行處理。

//定義輸入的數據源
        File inFile = new File("e:/in.txt");
        File outFile = new File("e:/out.txt");
        try {
            //獲得隧道
            FileChannel inFc = new FileInputStream(inFile).getChannel();
            FileChannel outFc = new FileOutputStream(outFile).getChannel();

            //定義一個ByteBuffer貨車出來,必須給貨車分配容量,才能更快。
            ByteBuffer bb = ByteBuffer.allocate(1024*20);
            bb.asIntBuffer().put(new int[]{1,2,3,4});
            //此處寫出後,在文本中看見的是亂碼,因爲輸出的是int(4字節)的而二進制文件,文本不
            //能顯示二進制文件,所以亂碼
            outFc.write(bb);

            //讀取數據,可以一個一個或者成批次的讀取
            bb.flip();
            int i = 0;
            IntBuffer ib = bb.asIntBuffer();
            while((i = ib.get()) != 0){
                System.out.println(i);
            }
            //成批次獲取
            ib.flip();
            //數組的大小必須符合ByteBuffer中數據的數量,不然會報錯
            int[] temp = new int[ib.limit()];
            ib.get(temp);
            for(int j : temp){
                System.out.print(j);
            }

            inFc.close();
            outFc.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }

 

3.3內存映射文件

    由於虛擬機內存太小,或者某些數據太大,導致不能將某些數據放入內存,所以需要內存映射文件,他能直接在內存中映射大文件,可以把它當作非常大的數組訪問。

    在寫入時,可以使用FileOutputStream,但映射文件的輸出則必須使用RandomAccessFile。所以說,最好使用RandomAccessFile

try {
            //先獲取隧道
            FileChannel fc = new RandomAccessFile("e:/out.txt","rw").getChannel();
            //通過隧道獲取MappedByteBuffer,此類繼承自ByteBuffer,是一個抽象類,表示內存映射文件
            //三個參數分別爲:使用權限,起始映射位置,映射大小
            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE,0,1024);
            IntBuffer ib = mbb.asIntBuffer();
            ib.put(1);
            fc.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }

3.4文件加鎖

    NIO中由文件加鎖機制,可以保證文件的同步或者異步訪問。鎖能直接映射到系統的文件鎖,所以無論是不是Java線程,都不能訪問加鎖後的文件。

try {
            //先獲取隧道
            FileChannel fc = new RandomAccessFile("e:/out.txt","rw").getChannel();
            //tryLock是非阻塞式的,當不可獲得時會返回。

            //position - 鎖定區域開始的位置;必須爲非負數
            //size - 鎖定區域的大小;必須爲非負數,並且 position + size 的和必須爲非負數
            //shared - 要請求共享鎖定,則爲 true,在這種情況下此通道必須允許進行讀取(可能是寫入)操作;
            // 要請求獨佔鎖定,則爲 false,在這種情況下此通道必須允許進行寫入(可能是讀取)操作
            
            FileLock fl1 = fc.tryLock(0,512,true);
            //lock是阻塞式的,他將阻塞進程,直到可以獲得鎖,或者調用lock的通道被關閉
            FileLock fl2 = fc.lock();
            //釋放鎖
            fl1.release();
            fl2.release();

            fc.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章