Java输入输出系统之nio(二)缓冲器基本数据类型存取、视图缓冲器和字节序

上一篇《Java输入输出系统之nio(一)通道和缓冲器》学习了通道和缓冲器的使用以及缓冲器的底层原理,本篇文章将学习缓冲器基本数据类型存取、视图缓冲器和字节序。

缓冲器基本数据类型存取

ByteBuffer除了可以存取字节以外还可以存取基本数据类型,存取基本数据类型的方法有:

public ByteBuffer putChar(char value);
public char getChar();

public ByteBuffer putShort(short value);
public short getShort();

public ByteBuffer putInt(int value);
public int getInt();

public ByteBuffer putLong(long value);
public long getLong();

public ByteBuffer putFloat(float value);
public float getFloat();

public ByteBuffer putDouble(double value);
public double getDouble();

使用示例如下:

ByteBuffer bb = ByteBuffer.allocate(512);
bb.putChar('O');
bb.putInt(1024);
bb.putDouble(10.24);

bb.flip();
System.out.println("getChar: " + bb.getChar());
System.out.println("getInt: " + bb.getInt());
System.out.println("getDouble: " + bb.getDouble());
/*输出:
getChar: O
getInt: 1024
getDouble: 10.24

上面的示例代码举例说明了char、int、double数据类型的存取,其他的基本数据类型的存取方法也是一样的。有时候需要存取一组基本数据类型,比如一个int数组,ByteBuffer没有提供直接存取一组基本数据类型的方法,只能通过循环语句一个一个的添加。

视图缓冲器

缓冲器除了ByteBuffer以外还有以下几种类型:

  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

上面的缓冲器可以很方便的存取基本数据类型,比如可以存取一组基本数据类型。视图缓冲器是指把ByteBuffer转换为其它的缓冲器,使用其它缓冲器的方法来存取数据,但是底层的数据存储还是ByteBuffer,对视图缓冲器的任何修改都会反映在ByteBuffer上。视图缓冲器类似数据库的视图,它提供了另外的一种视角来展现数据。ByteBuffer转换为视图缓冲器的方法如下:

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

ByteBuffer虽然可以存取基本数据类型,但是一次只能存取一个,有了视图缓冲器以后就可以存储一组基本数据类型了,存取示例如下:

ByteBuffer bb = ByteBuffer.allocate(512);
IntBuffer ib = bb.asIntBuffer();
ib.put(new int[] {1,2,3,4,5,6,7});
int[] array = new int[7];
ib.flip();
ib.get(array);
for (int i: array) {
    System.out.println(i);
}

/*输出:
1
2
3
4
5
6
7
 */

当ByteBuffer转换为视图缓冲器时内部成员变量position、limit、capacity是怎么转换的呢?看下面的代码:

ByteBuffer bb = ByteBuffer.allocate(512);
bb.putInt(10);
System.out.println(String.format("ByteBuffer init: position = %d limit = %d capacity = %d",
        bb.position(),
        bb.limit(),
        bb.capacity()));


IntBuffer ib = bb.asIntBuffer();
System.out.println(String.format("IntBuffer  init: position = %d limit = %d capacity = %d",
        ib.position(),
        ib.limit(),
        ib.capacity()));
ib.put(new int [] {1,2,3,4});


System.out.println(String.format("ByteBuffer last: position = %d limit = %d capacity = %d",
        bb.position(),
        bb.limit(),
        bb.capacity()));
/*输出:
ByteBuffer init: position = 4 limit = 512 capacity = 512
IntBuffer  init: position = 0 limit = 127 capacity = 127
ByteBuffer last: position = 4 limit = 512 capacity = 512
 */

从程序输出可以看到,ByteBuffer转换为IntBuffer时IntBuffer的limit和capacity值为ByteBuffer的剩余容量除以4,本例中为 (512 - 4) / 4 = 127。而position被设置为0,其对应ByteBuffer的位置4,见下图:

使用IntBuffer存储一组数据以后,ByteBuffer的成员变量并没有发生变化,这是因为ByteBuffer和IntBuffer的内部成员变量是相互独立变化的,例如,往ByteBuffer添加数据会改变它的position,但是不会改变它的视图缓冲器的position,反过来往视图缓冲器添加数据会改变它的position但是不会改变ByteBuffer的position。

字节序

多字节数据类型在内存中的字节存储顺序叫作字节序。例如Int型变量需要使用4个字节表示,变量1的16进制为0x00000001,占用的4个字节从高到低为分别为0x00 0x00 0x00 0x01。要把1存储在内存中有两种存放方式:先存低字节和先存高字节,先存低字节的方式称为小端存储,先存高字节的的方式称为大端存储或者网络字节序。小端存储的低字节对应内存的低地址,高字节对应高地址。大端存储的高字节对应内存的低地址,低字节对应内存的高地址。下图是大小端存储示例:

ByteBuffer可以设置字节序,默认的字节序是大端,可以通过order方法来设置和获取字节序:

//获取字节序
public ByteOrder order();
//设置字节序
public ByteBuffer order(ByteOrder bo);

下面是字节序查询和设置示例:

teBuffer bb = ByteBuffer.allocate(512);
System.out.println(bb.order());

bb.order(ByteOrder.LITTLE_ENDIAN);
System.out.println(bb.order());
/*输出:
BIG_ENDIAN
LITTLE_ENDIAN
 */

下面是大小端字节序对ByteBuffer的影响示例:

ByteBuffer bb = ByteBuffer.allocate(512);
bb.putInt(1);
bb.flip();
System.out.print(bb.order());
System.out.print(": ");
while(bb.hasRemaining()) {
    System.out.print(bb.get());
    System.out.print(" ");
}
System.out.println();

bb.clear();
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(1);
bb.flip();
System.out.print(bb.order());
System.out.print(": ");
while(bb.hasRemaining()) {
    System.out.print(bb.get());
    System.out.print(" ");
}
/*输出:
BIG_ENDIAN: 0 0 0 1 
LITTLE_ENDIAN: 1 0 0 0 
 */

上例中,当使用大端字节序时优先存储高字节即高字节对应低地址,所以大端的输出为0 0 0 1。当使用小端字节序时优先存储低字节即低字节对应低地址,所以小端的输出为1 0 0 0。

ByteBuffer的字节序在读写二进程数据时非常有用,例如从磁盘读取一个wav格式的文件,wav文件是按照小端字节序存储数据的,读写wav文件时只需把ByteBuffer指定为小端字节序即可。

最后

使用通道和缓冲器读写文件具有很高的效率,只有理解了缓冲器的基本原理才能正确使用缓冲器。ByteBuffer是直接与通道交互的缓冲器,它可以转换为视图缓冲器来批量存储和读取基本数据类型。通过设置字节序,缓冲器可以读写特点格式的二进制文件。

 

【水煮Java】
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值