内容目录
2.4 字节缓冲区
在本章节中,我们将进一步观察字节缓冲区。所有的基本数据类型都有相应的缓冲区类(布尔型除外),但字节缓冲区有自己的独特之处。字节是操作系统及其 I/O 设备使用的基本数据类型。当在 JVM 和操作系统间传递数据时,将其他的数据类型拆分成构成它们的字节是十分必要的。如我们在后面的章节中将要看到的那样,系统层次的 I/O 面向字节的性质可以在整个缓冲区的设计以及它们互相配合的服务中感受到。
为了提供参考,以下是ByteBuffer
的完整 API。这些函数有些已经在前面的章节中讨论,并且仅仅是针对具体类型的版本。新的函数将在本节以及后面的章节中涉及。
package java.nio;
public abstract class ByteBuffer extends Buffer implements Comparable
{
public static ByteBuffer allocate (int capacity)
public static ByteBuffer allocateDirect (int capacity)
public abstract boolean isDirect();
public static ByteBuffer wrap (byte[] array, int offset, int length)
public static ByteBuffer wrap (byte[] array)
public abstract ByteBuffer duplicate();
public abstract ByteBuffer asReadOnlyBuffer();
public abstract ByteBuffer slice();
public final boolean hasArray()
public final byte [] array()
public final int arrayOffset()
public abstract byte get();
public abstract byte get (int index);
public ByteBuffer get (byte[] dst, int offset, int length)
public ByteBuffer get (byte[] dst, int offset, int length)
public abstract ByteBuffer put (byte b);
public abstract ByteBuffer put (int index, byte b);
public ByteBuffer put (ByteBuffer src)
public ByteBuffer put (byte[] src, int offset, int length)
public final ByteBuffer put (byte[] src)
public final ByteOrder order()
public final ByteBuffer order (ByteOrder bo)
public abstract CharBuffer asCharBuffer();
public abstract ShortBuffer asShortBuffer();
public abstract IntBuffer asIntBuffer();
public abstract LongBuffer asLongBuffer();
public abstract FloatBuffer asFloatBuffer();
public abstract DoubleBuffer asDoubleBuffer();
public abstract char getChar();
public abstract char getChar(int index);
public abstract ByteBuffer putChar (char value);
public abstract ByteBuffer putChar (int index, char value);
public abstract short getShort();
public abstract short getShort (int index);
public abstract ByteBuffer putShort (short value);
public abstract ByteBuffer putShort (int index, short value);
public abstract int getInt();
public abstract int getInt (int index);
public abstract ByteBuffer putInt (int value);
public abstract ByteBuffer putInt (int index, int value);
public abstract long getLong();
public abstract long getLong (int index);
public abstract ByteBuffer putLong (long value);
public abstract ByteBuffer putLong (int index, long value);
public abstract float getFloat();
public abstract float getFloat (int index);
public abstract ByteBuffer putFloat (float value);
public abstract ByteBuffer putFloat (int index, float value);
public abstract double getDouble();
public abstract double getDouble (int index);
public abstract ByteBuffer putDouble (double value);
public abstract ByteBuffer putDouble (int index, double value);
public abstract ByteBuffer compact();
public boolean equals (Object ob)
public int compareTo (Object ob)
public String toString()
public int hashCode()
}
字节总是八位的,对吗? 目前,字节几乎被广泛认为是八个比特位。但这并非一直是实情。在过去的时代,每个字节可以是3到12之间任何个数或者更多个的比特位,最常见的是6到9位。八位的字节来自于市场力量和实践的结合。它之所以实用是因为8位足以表达可用的字符集(至少是英文字符),8是2 的三次乘方(这简化了硬件设计),八恰好容纳两个十六进制数字,而且8的倍数提供了足够的组合位来存储有效的数值。最初的市场推广力量是IBM。在 1960 年首先推出的 IBM360 大型机使用的就是8位字节。
2.4.1 字节顺序
非字节类型的基本类型,除了布尔型都是由组合在一起的几个字节组成的。这些数据类型及其大小总结在表 2-1 中。
表 2-1.基本数据类型及其大小数据类型大小(以字节表示) | |
---|---|
数据类型 | 大小(以字节表示) |
Byte | 1 |
Char | 2 |
Short | 2 |
Int | 4 |
Long | 8 |
Float | 4 |
Double | 8 |
每个基本数据类型都是以连续字节序列的形式存储在内存中。例如,32位的int值0x037fb4c7(十进制的58,700,999)可能会如图 2-14 所显示的那样被塞入内存字节中(内存地址从左往右增加)。注意前一个句子中的“可能”一词。尽管字节大小已经被确定,但字节顺序问题一直没有被广泛认同。表示一个整型值的字节可能在内存中仅仅如图 2-15 所示的那样被简单地排列。
布尔型代表两种值:true
或false
。一个字节可以包含256个唯一值,所以一个布尔值不能准确地对应到一个或多个字节上。字节是所有缓冲区的构造块。NIO 架构决定了布尔缓冲区的执行是有疑问的,而且对这种缓冲区类型的需求不管怎么说都是颇具争议的。
多字节数值被存储在内存中的方式一般被称为endian-ness(字节顺序)。如果数字数值的最高字节——big end(大端),位于低位地址,那么系统就是大端字节顺序(如图 2-14 所示)。如果最低字节最先保存在内存中,那么小端字节顺序(如图 2-15 所示)。
字节顺序很少由软件设计者决定;它通常取决于硬件设计。字节顺序的两种类型有时被称为字节性别,在当今被广泛使用。两种方式都具有自身的优势。Intel 处理器使用小端字节顺序涉及。摩托罗拉的CPU系列、SUN的Sparc工作站,以及PowerPC的CPU架构都采用大端字节顺序。
字节顺序的问题甚至胜过CPU硬件设计。当Internet的设计者为互联各种类型的计算机而设计网际协议(IP)时,他们意识到了在具有不同内部字节顺序的系统间传递数值数据的问题。因此,IP协议规定了使用大端的网络字节顺序概念。所有在IP分组报文的协议部分中使用的多字节数值必须先在本地主机字节顺序和通用的网络字节顺序之间进行转换。
在java.nio中,字节顺序由ByteOrder类封装。
package java.nio; public final class ByteOrder { public static final ByteOrder BIG_ENDIAN public static final ByteOrder LITTLE_ENDIAN public static ByteOrder nativeOrder( ) public String toString( ) }
ByteOrder
类定义了决定从缓冲区中存储或检索多字节数值时使用哪一字节顺序的常量。这个类的作用就像一个类型安全的枚举。它定义了以其本身实例预初始化的两个public
区域。只有这两个ByteOrder
实例总是存在于 JVM 中,因此它们可以通过使用--操作符进行比较。如果您需要知道JVM运行的硬件平台的固有字节顺序,请调用静态类函数nativeOrder()
。它将返回两个已确定常量中的一个。调用toString()
将返回一个包含两个文字字符串BIG_ENDIAN
或者LITTLE_ENDIAN
之一的String
。
每个缓冲区类都具有一个能够通过调用order()
查询的当前字节顺序设定。
public abstract class CharBuffer extends Buffer implements Comparable, CharSequence
{
// 这里仅列出部分API
public final ByteOrder order()
}
因特网技术将字节成为八位位组(octet)。如侧栏所介绍的,一个字节的大小是有歧义的。通过使用“octet”这一术语,IP规则明确表明字节由八个位组成。
这个函数从ByteOrder返回两个常量之一。对于除了ByteOrder
之外的其他缓冲区类,字节顺序是一个只读属性,并且可能根据缓冲区的建立方式而采用不同的值。除了ByteBuffer
,其他通过分配或包装一个数组所创建的缓冲区将从order()
返回与ByteOrder.nativeOrder()
相同的数值。这使因为包含在缓冲区中的元素在JVM中将会被作为基本数据直接存取。
ByteBuffer
类有所不同:默认字节顺序总是ByteBuffer.BIG_ENDIAN
,无论系统的固有字节顺序是什么。Java的默认字节顺序是大端字节顺序,这允许类文件等以及串行化的对象可以在任何JVM中工作。如果固有硬件字节顺序是小端,这会有性能隐患。在使用固有硬件字节顺序时,将ByteBuffer
的内容当作其他数据类型存取(很快就会讨论到)很可能高效得多。 很可能您会对为什么ByteBuffer
类需要一个字节顺序设定这一问题感到困惑。字节就是字节,对吗?当然,但是如您不久将在 2.4.4 节所看到的那样,ByteBuffer
对象像其他基本数据类型一样,具有大量便利的函数用于获取和存放缓冲区内容。这些函数对字节进行编码或解码的方式取决于 ByteBuffer
当前字节顺序的设定。ByteBuffer
的字符顺序设定可以随时通过调用以ByteOrder.BIG_ENDIAN
或ByteOrder.LITTL_ENDIAN
为参数的order()
函数来改变。
public abstract class ByteBuffer extends Buffer implements Comparable
{
// 这里仅列出部分API
public final ByteOrder order()
public final ByteBuffer order (ByteOrder bo)
}
如果一个缓冲区被创建为一个ByteBuffer
对象的视图(参见 2.4.3 节),那么order()
返回的数值就是视图被创建时其创建源头的 ByteBuffer
的字节顺序设定。视图的字节顺序设定在创建后不能被改变,而且如果原始的字节缓冲区的字节顺序在之后被改变,它也不会受到影响。
0 条评论
撰写评论