ホームページ  >  記事  >  Java  >  Java NIO バッファのコードとテキストの詳細な説明

Java NIO バッファのコードとテキストの詳細な説明

黄舟
黄舟オリジナル
2017-03-31 11:09:121684ブラウズ

バッファオブジェクトは、固定量のデータのコンテナです。チャネルは I/O 転送が行われるエントリであり、バッファはこれらのデータ転送の送信元または宛先です。

バッファの基本

すべてのバッファには、バッファに含まれるデータ要素に関する情報を提供する 4 つの プロパティがあります。

  • capacity (容量): バッファーが保持できるデータの最大値。バッファーの作成後に変更することはできません。

  • limit (上限): 読み取りまたは書き込みができないバッファーの最初の要素。あるいは、バッファー内の既存の要素の数。

  • position (位置): 読み書きされる次の要素の index。 get または put 関数 を呼び出すと 更新 が行われます。

  • マーク(マーク):メモの場所。 mark() を呼び出して、mark=postion を設定します。 Reset() を呼び出して、position= マークを設定します。タグは設定されるまで未定義です。

これら 4 つのプロパティの間には常に次の関係があります:

0 <= mark <= position <= limit <= capacity

下の図は新しく作成された ByteBuffer です:

位置は 0 に設定され、容量と上限は 10 に設定されます, バッファーが保持できる最後のバイトを十分に超えています。 タグは最初は未定義です。容量は固定ですが、他の 3 つのプロパティはバッファーを使用するときに変更できます。

以下は Buffer のメソッド シグネチャです:

public abstract class Buffer {

    public final int capacity() {
    }

    public final int position() {
    }

    public final Buffer position(int newPosition) {
    }

    public final int limit() {
    }

    public final Buffer limit(int newLimit) {
    }

    public final Buffer mark() {
    }

    public final Buffer reset() {
    }

    public final Buffer clear() {
    }

    public final Buffer flip() {
    }

    public final Buffer rewind() {
    }

    public final int remaining() {
    }

    public final boolean hasRemaining() {
    }

    public abstract boolean isReadOnly();

    public abstract boolean hasArray();

    public abstract Object array();

    public abstract int arrayOffset();

    public abstract boolean isDirect();
}

上記の Buffer API には get() 関数や put() 関数は含まれていません。すべての Buffer クラスにはこれら 2 つの関数がありますが、関数が受け取るパラメータの型と返すデータ型は各サブクラスに固有であるため、最上位の Buffer クラスのステートメントで抽象化することはできません。

以下は ByteBuffer の宣言です:

public abstract class ByteBuffer extends Buffer implements Comparable
{
    // This is a partial API listing
    public abstract byte get( );
    public abstract byte get (int index);
    public abstract ByteBuffer put (byte b);
    public abstract ByteBuffer put (int index, byte b);
}

ByteBuffer ストレージの例:

buffer.put((byte)&#39;H&#39;).put((byte)&#39;e&#39;).put((byte)&#39;l&#39;).put((byte)&#39;l&#39;).put((byte)&#39;o&#39;);

次の操作を実行している場合:

buffer.put(0,(byte)&#39;M&#39;).put((byte)&#39;w&#39;);

バッファがいっぱいで、バッファを反転する必要がある場合は、ByteBuffer の反転メソッドを呼び出すことができます。

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

rewind() 関数は、flip() に似ていますが、影響はありません。上限プロパティ。位置の値を 0 に戻すだけです。 rewind() を使用すると、反転されたバッファ内のデータを再度読み取ることができます。

hasRemaining() メソッドは、バッファーの上限に達したかどうかを返します。

remaining() メソッドは、上限までの距離を返します。

mark() 関数が呼び出される前はバッファのマークは未定義であり、呼び出されたときにマークは現在位置の値に設定されます。

reset() 関数は、位置を現在のマーカー値に設定します。マーク値が未定義の場合、reset() を呼び出すと InvalidMarkException 例外が発生します。

rewind()、clear()、flip() は常にフラグを破棄します。つまり、-1 に設定されます。

2 つのバッファーが等しいとみなされるための必要十分条件は次のとおりです:

  • 2 つのオブジェクトは同じタイプです。異なるデータ型を含むバッファーは決して同等ではなく、バッファーが非バッファー オブジェクトと等しくなることはありません。

  • 両方のオブジェクトには同じ数の要素が残っています。バッファの容量は同じである必要はなく、バッファ内の残りのデータのインデックスも同じである必要はありません。ただし、各バッファー内の残りの要素の数 (位置から上限まで) は同じでなければなりません。

  • Get() 関数によって返される必要がある残りのデータ要素のシーケンスは、各バッファーで一貫している必要があります。

バッファの作成

//分配一个容量为 100 个 char 变量的 Charbuffer
CharBuffer charBuffer = CharBuffer.allocate (100);

char [] myArray = new char [100];
CharBuffer charbuffer = CharBuffer.wrap (myArray);
//这段代码构造了一个新的缓冲区对象,但数据元素会存在于数组中。
这意味着通过调用 put()函数造成的对缓冲区的改动会直接影响这个数组,而且对这个数组的任何改动也会对这 个缓冲区对象可见。

CharBuffer.wrap(array, offset, length);可以指定 position 和 length

assign() または Wrap() 関数を通じて作成されたバッファは通常間接的であり、間接バッファはバックアップ配列を使用します。 hasArray() このバッファにアクセス可能なバッキング配列があるかどうかを返します。

コピーバッファ

いくつかのコピーバッファAPI:

public abstract class CharBuffer extends Buffer implements CharSequence, Comparable {
	// This is a partial API listing
	public abstract CharBuffer duplicate( );
	public abstract CharBuffer asReadOnlyBuffer( );
	public abstract CharBuffer slice( );
}

duplicate() 函数创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。

如下示例:

public static void main(String[] args) {
		CharBuffer buffer = CharBuffer.allocate(8);
		buffer.position(3).limit(6).mark().position(5);
		CharBuffer dupeBuffer = buffer.duplicate();
		System.out.println(dupeBuffer.position());
		System.out.println(dupeBuffer.limit());
		dupeBuffer.clear();
		System.out.println(dupeBuffer.position());
		System.out.println(dupeBuffer.limit());
	}

//out
5
6
0
8

asReadOnlyBuffer() 函数来生成一个只读的缓冲区图。

代码说明如下:

public static void main(String[] args) {
		CharBuffer buffer = CharBuffer.allocate(8);
		CharBuffer dupeBuffer = buffer.asReadOnlyBuffer();
		System.out.println(dupeBuffer.isReadOnly());
		dupeBuffer.put(&#39;S&#39;);//只读buffer调用抛出异常
	}

//out
true
Exception in thread "main" java.nio.ReadOnlyBufferException
	at java.nio.HeapCharBufferR.put(HeapCharBufferR.java:172)
	at nio.test.TestMain.main(TestMain.java:10)

slice() 创建一个从原始缓冲区的当前位置开始的新缓冲 区,并且其容量是原始缓冲区的剩余元素数量(limit-position)。这个新缓冲区与原始 缓冲区共享一段数据元素子序列。分出来的缓冲区也会继承只读和直接属性。

public static void main(String[] args) {
		CharBuffer buffer = CharBuffer.allocate(8);
		buffer.position(3).limit(5);
		CharBuffer sliceBuffer = buffer.slice();
	}

字节缓冲区

在 java.nio 中,字节顺序由 ByteOrder 类封装。

ByteOrder.nativeOrder() 方法返回 JVM 运行的硬件平台字节顺序。

直接缓冲区

只有字节缓冲区有资格参与 I/O 操作。

I/O 操作的目标内存区域必须是连续的字节序列。

直接缓冲区被用于与通道和固有 I/O 例程交互。

直接字节缓冲区通常是 I/O 操作最好的选择。直接字节缓冲区支持 JVM 可用的最高效 I/O 机制。非直接字节缓冲区可以被传递给通道,但是这样可能导致性能耗。通常非直接缓冲不可能成为一个本地 I/O 操作的目标。如果向一个通道中传递一个非直接 ByteBuffer 对象用于写入会每次隐含调用下面的操作:

  1. 创建一个临时的直接 ByteBuffer 对象。

  2. 将非直接缓冲区的内容复制到直接临时缓冲中。

  3. 使用直接临时缓冲区执行低层次 I/O 操作。

  4. 直接临时缓冲区对象离开作用域,并最终成为被回的无用数据。

直接缓冲区时 I/O 的最佳选择,但可能比创建非直接缓冲区要花费更高的成本。直接缓冲区使用的内存是通过调用本地操作系统方面的代码分配的, 过了标准 JVM 。

ByteBuffer.allocateDirect() 创建直接缓冲区。isDirect() 返回是否直接缓冲区。

视图缓冲区

视图缓冲区通过已存在的缓冲区对象实例的工方法来创建。这种图对象维护它自己的属性,容量,位置,上界和标记,但是和原来的缓冲区共享数据元素。

ByteBuffer 类允许创建图来将 byte 型缓冲区字节数据映射为其它的原始数据类型。

public abstract CharBuffer asCharBuffer( );
public abstract ShortBuffer asShortBuffer( );
public abstract IntBuffer asIntBuffer( );
public abstract LongBuffer asLongBuffer( );
public abstract FloatBuffer asFloatBuffer( );
public abstract DoubleBuffer asDoubleBuffer( );

看如下一个例子的示意图:

ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer( );

public class BufferCharView {
       public static void main (String [] argv)  throws Exception  {
          ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN);
          CharBuffer charBuffer = byteBuffer.asCharBuffer( );
          // Load the ByteBuffer with some bytes
          byteBuffer.put (0, (byte)0);
          byteBuffer.put (1, (byte)&#39;H&#39;);
          byteBuffer.put (2, (byte)0);
          byteBuffer.put (3, (byte)&#39;i&#39;);
          byteBuffer.put (4, (byte)0);
          byteBuffer.put (5, (byte)&#39;!&#39;);
          byteBuffer.put (6, (byte)0);
          println (byteBuffer);
          println (charBuffer);
       }
       // Print info about a buffer
       private static void println (Buffer buffer)  {
		  System.out.println ("pos=" + buffer.position() + ", limit=" +
          	buffer.limit() + ", capacity=" + buffer.capacity() + ": &#39;" + buffer.toString( ) + "&#39;");
	}
}

//运行 BufferCharView 程序的输出是:
//pos=0, limit=7, capacity=7: &#39;java.nio.HeapByteBuffer[pos=0 lim=7 cap=7]&#39;
//pos=0, limit=3, capacity=3: &#39;Hi!

数据元素视图

ByteBuffer 类为每一种原始数据类型 供了存取的和转化的方法:

public abstract class ByteBuffer extends Buffer implements Comparable {
	public abstract char getChar();

	public abstract char getChar(int index);

	public abstract short getShort();

	public abstract short getShort(int index);

	public abstract int getInt();

	public abstract int getInt(int index);

	public abstract long getLong();

	public abstract long getLong(int index);

	public abstract float getFloat();

	public abstract float getFloat(int index);

	public abstract double getDouble();

	public abstract double getDouble(int index);

	public abstract ByteBuffer putChar(char value);

	public abstract ByteBuffer putChar(int index, char value);

	public abstract ByteBuffer putShort(short value);

	public abstract ByteBuffer putShort(int index, short value);

	public abstract ByteBuffer putInt(int value);

	public abstract ByteBuffer putInt(int index, int value);

	public abstract ByteBuffer putLong(long value);

	public abstract ByteBuffer putLong(int index, long value);

	public abstract ByteBuffer putFloat(float value);

	public abstract ByteBuffer putFloat(int index, float value);

	public abstract ByteBuffer putDouble(double value);

	public abstract ByteBuffer putDouble(int index, double value);
}

假如一个 bytebuffer 处于如下状态:

那么 int value = buffer.getInt();

实际的返回值取决于缓冲区的当前的比特排序(byte-order)设置。更具体的写法是:

int value = buffer.order (ByteOrder.BIG_ENDIAN).getInt( );

这将会返回值 0x3BC5315E,同时:

int value = buffer.order (ByteOrder.LITTLE_ENDIAN).getInt( );

返回值 0x5E31C53B

如果您试图获取的原始类型需要比缓冲区中存在的字节数更多的字节,会抛出 BufferUnderflowException。

以上がJava NIO バッファのコードとテキストの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。