程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Merlin的魔力: Merlin的新I/O緩沖區的輸入和輸出

Merlin的魔力: Merlin的新I/O緩沖區的輸入和輸出

編輯:關於JAVA

Java 2 平台標准版(Java 2 Platform Standard Edition,J2SE)1.4 對 Java 平台的 I/O 處理能力做了大量更改。它不僅用流到流的鏈接方式繼續支持以前 J2SE 發行版的基於流的 I/O 操作,而且 Merlin 還添加了新的功能 — 稱之為新 I/O 類(NIO),現在這些類位於 java.nio 包中。

I/O 執行輸入和輸出操作,將數據從文件或系統控制台等傳送至或傳送出應用程序。(有關 Java I/O 的其它信息,請參閱 參考資料)。

緩沖區基礎

抽象的 Buffer 類是 java.nio 包支持緩沖區的基礎。 Buffer 的工作方式就象內存中用於讀寫基本數據類型的 RandomAccessFile 。象 RandomAccessFile 一樣,使用 Buffer ,所執行的下一個操作(讀/寫)在當前某個位置發生。執行這兩個操作中的任一個都會改變那個位置,所以在寫操作之後進行讀操作不會讀到剛才所寫的內容,而會讀到剛才所寫內容之後的數據。 Buffer 提供了四個指示方法,用於訪問線性結構(從最高值到最低值):

capacity() :表明緩沖區的大小

limit() :告訴您到目前為止已經往緩沖區填了多少字節,或者讓您用 :limit(int newLimit) 來改變這個限制

position() :告訴您當前的位置,以執行下一個讀/寫操作

mark() :為了稍後用 reset() 進行重新設置而記住某個位置

緩沖區的基本操作是 get() 和 put() ;然而,這些方法在子類中都是針對每種數據類型的特定方法。為了說明這一情況,讓我們研究一個簡單示例,該示例演示了從同一個緩沖區讀和寫一個字符。在清單 1 中, flip() 方法交換限制和位置,然後將位置置為 0,並廢棄標記,讓您讀剛才所寫的數據:

清單 1. 讀/寫示例

import java.nio.*;
...
CharBuffer buff = ...;
buff.put('A');
buff.flip();
char c = buff.get();
System.out.println("An A: " + c);

現在讓我們研究一些具體的 Buffer 子類。

緩沖區類型

Merlin 具有 7 種特定的 Buffer 類型,每種類型對應著一個基本數據類型(不包括 boolean):

ByteBuffer

CharBuffer

DoubleBuffer

FloatBuffer

IntBuffer

LongBuffer

ShortBuffer

在本文後面,我將討論第 8 種類型 MappedByteBuffer ,它用於內存映射文件。如果您必須使用的類型不是這些基本類型,則可以先從 ByteBuffer 獲得字節類型,然後將其轉換成 Object 或其它任何類型。

正如前面所提到的,每個緩沖區包含 get() 和 put() 方法,它們可以提供類型安全的版本。通常,需要重載這些 get() 和 put() 方法。例如,有了 CharBuffer ,可以用 get() 獲得下一個字符,用 get(int index) 獲得某個特定位置的字符,或者用 get(char[] destination) 獲得一串字符。靜態方法也可以創建緩沖區,因為不存在構造函數。那麼,仍以 CharBuffer 為例,用 CharBuffer.wrap(aString) 可以將 String 對象轉換成 CharBuffer 。為了演示,清單 2 接受第一個命令行參數,將它轉換成 CharBuffer ,並顯示參數中的每個字符:

清單 2. CharBuffer 演示

import java.nio.*;
public class ReadBuff {
  public static void main(String args[]) {
   if (args.length != 0) {
    CharBuffer buff = CharBuffer.wrap(args[0]);
    for (int i=0, n=buff.length(); i<n; i++) {
     System.out.println(i + " : " + buff.get());
    }
   }
  }
}

請注意,這裡我使用了 get() ,而沒有使用 get(index) 。我這樣做的原因是,在每次執行 get() 操作之後,位置都會移動,所以不需要手工來聲明要檢索的位置。

直接 vs. 間接

既然已經了解了典型的緩沖區,那麼讓我們研究直接緩沖區與間接緩沖區之間的差別。在創建緩沖區時,可以要求創建直接緩沖區,創建直接緩沖區的成本要比創建間接緩沖區高,但這可以使運行時環境直接在該緩沖區上進行較快的本機 I/O 操作。因為創建直接緩沖區所增加的成本,所以直接緩沖區只用於長生存期的緩沖區,而不用於短生存期、一次性且用完就丟棄的緩沖區。而且,只能在 ByteBuffer 這個級別上創建直接緩沖區,如果希望使用其它類型,則必須將 Buffer 轉換成更具體的類型。為了演示,清單 3 中代碼的行為與清單 2 的行為一樣,但清單 3 使用直接緩沖區:

清單 3. 列出網絡接口

import java.nio.*;
public class ReadDirectBuff {
  public static void main(String args[]) {
   if (args.length != 0) {
    String arg = args[0];
    int size = arg.length();
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(size*2);
    CharBuffer buff = byteBuffer.asCharBuffer();
    buff.put(arg);
    buff.rewind();
    for (int i=0, n=buff.length(); i<n; i++) {
     System.out.println(i + " : " + buff.get());
    }
   }
  }
}

在上面的代碼中,請注意,不能只是將 String 包裝在直接 ByteBuffer 中。必須首先創建一個緩沖區,先填充它,然後將位置倒回起始點,這樣才能從頭讀。還要記住,字符長度是字節長度的兩倍,因此示例中會有 size*2 。

內存映射文件

第 8 種 Buffer 類型 MappedByteBuffer 只是一種特殊的 ByteBuffer 。 MappedByteBuffer 將文件所在區域直接映射到內存。通常,該區域包含整個文件,但也可以只映射部分文件。所以,必須指定要映射文件的哪部分。而且,與其它 Buffer 對象一樣,這裡沒有構造函數;必須讓 java.nio.channels.FileChannel 的 map() 方法來獲取 MappedByteBuffer 。此外,無需過多涉及通道就可以用 getChannel() 方法從 FileInputStream 或 FileOutputStream 獲取 FileChannel 。通過從命令行傳入文件名來讀取文本文件的內容,清單 4 顯示了 MappedByteBuffer :

清單 4. 讀取內存映射文本文件

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class ReadFileBuff {
  public static void main(String args[]) throws IOException {
   if (args.length != 0) {
    String filename = args[0];
    FileInputStream fis = new FileInputStream(filename);
    FileChannel channel = fis.getChannel();
    int length = (int)channel.size();
    MappedByteBuffer byteBuffer =
     channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
    Charset charset = Charset.forName("ISO-8859-1");
    CharsetDecoder decoder = charset.newDecoder();
    CharBuffer charBuffer = decoder.decode(byteBuffer);
    for (int i=0, n=charBuffer.length(); i<n; i++) {
     System.out.print(charBuffer.get());
    }
   }
  }
}

正如我在“字符集”(請參閱 參考資料)這篇文章中所解釋的,由於文件有內容,必須告訴系統如何將字節轉換成字符。因此需要使用 Charset 。

結束語

由 Buffer 支撐的 J2SE 新的 I/O 包從根本上改變了 Java 技術處理 I/O 操作的方式。在閱讀完本文之後,您應該了解了 NIO 從基本的 get 和 put 操作到讀取內存映射文件方面的知識。然而,這不能說,學習 NIO 就到此為止了。使用這裡所提及的參考資料來研讀 Java 平台內的 I/O。在以後的文章中,我將把這裡所提到的概念應用到套接字通道的使用當中。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved