slice()方法的作用:创建新的字节缓冲区,其内容是此缓冲区内容的共享子序列。新缓冲区的内容将从此缓冲区的当前位置开始。此缓冲区的内容的更改在新缓冲区中是可见的;但是这两个缓冲区的位置、限制、标记是相互独立的。新缓冲区的位置将为0,其容量和限制将为此缓冲区中剩余的字节数,其标记是不确定的。当且仅当此缓冲区为直接缓冲区时,新缓冲区才为直接缓冲区。当且仅当此缓冲区为只读缓冲区,新缓冲区才为只读缓冲区。
public class SliceDemo {
public static void main(String[] args) {
byte[] array1 = {1,2,3,4,5,6,7,8};
ByteBuffer buffer = ByteBuffer.wrap(array1);
buffer.position(5);
ByteBuffer slice = buffer.slice();
System.out.println("buffer1 capacity = " + buffer.capacity() +
" limit="+buffer.limit()+" position="+buffer.position());
System.out.println("slice capacity = " + slice.capacity() +
" limit="+slice.limit()+" position="+slice.position());
System.out.println(Arrays.toString(buffer.array()));
System.out.println(Arrays.toString(slice.array()));
}
}
在使用slice()方法后,在调用arrayOffset方法会出现返回的值为非0的情况。测试代码:
public class AraayOffsetDemo {
public static void main(String[] args) {
byte[] array1 = {1,2,3,4,5,6,7,8};
ByteBuffer buffer = ByteBuffer.wrap(array1);
buffer.position(5);
ByteBuffer buffer1 = buffer.slice();
System.out.println(buffer1.arrayOffset());
}
}
运行结果说明buffer1的第一个元素的位置是相对于array1数组中的索引值为5偏移的。
asCharBuffer()方法的作用:创建此字节缓冲区的视图,作为char缓冲区。新缓冲区的内容将从此缓冲区的当前位置开始。此缓冲区的内容更新在新缓冲区是可见的;这两个缓冲区的位置、限制、标记是相互独立的。新缓冲区的位置将为0,其容量和限制将为此缓冲区所剩字节的1/2,其标记是不确定的。当且仅当此缓冲区为直接缓冲区时,新缓冲区才为直接缓冲区。当且仅当此缓冲区为只读缓冲区,新缓冲区才为只读缓冲区。
接下来看个例子:
public class AsCharBufferDemo {
public static void main(String[] args) {
byte[] array1 = "有情铁手".getBytes();
//查看默认的编码格式
System.out.println(Charset.defaultCharset().name());
ByteBuffer buffer = ByteBuffer.wrap(array1);
System.out.println("byteBuffer="+buffer.getClass().getName());
CharBuffer charBuffer = buffer.asCharBuffer();
System.out.println("charBuffer="+charBuffer.getClass().getName());
System.out.println("buffer capacity = " + buffer.capacity() +
" limit="+buffer.limit()+" position="+buffer.position());
System.out.println("charBuffer capacity = " + charBuffer.capacity() +
" limit="+charBuffer.limit()+" position="+charBuffer.position());
System.out.println(charBuffer.capacity());
charBuffer.position(0);
for (int i = 0; i < charBuffer.capacity(); i++) {
//这里会乱码,因为get方法采用的是UTF-16BE编码
//而默认的是UTF-8编码
//所以这里会乱码
System.out.print(charBuffer.get()+"");
}
}
}
运行上述程序会出现乱码的问题
只需要稍微修改一下就可以解决乱码的问题:
public class AsCharBufferDemo {
public static void main(String[] args) {
byte[] array1 = "有情铁手".getBytes("UTF-16BE");
//查看默认的编码格式
System.out.println(Charset.defaultCharset().name());
ByteBuffer buffer = ByteBuffer.wrap(array1);
System.out.println("byteBuffer="+buffer.getClass().getName());
CharBuffer charBuffer = buffer.asCharBuffer();
System.out.println("charBuffer="+charBuffer.getClass().getName());
System.out.println("buffer capacity = " + buffer.capacity() +
" limit="+buffer.limit()+" position="+buffer.position());
System.out.println("charBuffer capacity = " + charBuffer.capacity() +
" limit="+charBuffer.limit()+" position="+charBuffer.position());
System.out.println(charBuffer.capacity());
charBuffer.position(0);
for (int i = 0; i < charBuffer.capacity(); i++) {
//这里会乱码,因为get方法采用的是UTF-16BE编码
//而默认的是UTF-8编码
//所以这里会乱码
System.out.print(charBuffer.get()+"");
}
}
}
当然代码还可以这样修改
public class AsCharBufferDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
byte[] array1 = "有情铁手".getBytes("utf-8");
//查看默认的编码格式
System.out.println(Charset.defaultCharset().name());
ByteBuffer buffer = ByteBuffer.wrap(array1);
System.out.println("byteBuffer="+buffer.getClass().getName());
CharBuffer charBuffer = Charset.forName("utf-8").decode(buffer);
System.out.println("charBuffer="+charBuffer.getClass().getName());
System.out.println("buffer capacity = " + buffer.capacity() +
" limit="+buffer.limit()+" position="+buffer.position());
System.out.println("charBuffer capacity = " + charBuffer.capacity() +
" limit="+charBuffer.limit()+" position="+charBuffer.position());
System.out.println(charBuffer.capacity());
charBuffer.position(0);
for (int i = 0; i < charBuffer.limit(); i++) {
System.out.print(charBuffer.get()+"");
}
}
}
order()方法与字节排列顺序有关,因为不同的CPU读取字节时的顺序是不一样的,有的CPU从高位开始读取,而有的CPU从低位开始读取,当两种CPU传递数据时就是要将字节排列的顺序进行统一,此时order(ByteOrder bo) 方法就有了用武之地,它的作用就是设置字节的排列顺序。
那么什么是高位,什么是低位呢?如果是16位(双字节)的数据,如FF1A,高位就是FF,低位就是1A。如果是32位的数据,如3F68415B,高位字节就是3F68,低位字节就是415B,右边是低位,左边是高位。
ByteOrder order()方法的作用:获取此缓冲区的字节顺序。新创建的字节缓冲区的顺序始终为BIG_ENDIAN。在读取多字节以及以此字节缓冲区创建视图缓冲区时,使用该字节顺序。
asReadOnlyBuffer()方法的作用:创建共享此缓冲区内容的新的只读字节缓冲区。新缓冲区的内容将为此缓冲区的内容。此缓冲区的内容更新在新缓冲区是可见的,担心缓冲区将是只读且不允许修改共享内容。这两个缓冲区的位置、限制、标记是相互独立的。新缓冲区的位置、限制、标记等值将与此缓冲区相等。
public class ReadOnlydemo {
public static void main(String[] args) {
byte[] array1 = {1,2,3,4,5};
ByteBuffer wrap = ByteBuffer.wrap(array1);
ByteBuffer readOnlyBuffer = wrap.asReadOnlyBuffer();
System.out.println(wrap.isReadOnly());
System.out.println(readOnlyBuffer.isReadOnly());
readOnlyBuffer.rewind();
readOnlyBuffer.put((byte)123);
}
}
compact()方法的作用:压缩此缓冲区,将缓冲区的当前位置和限制之间的字节复制到缓冲区开始的位置,即将索引p=position()处的字节复制到索引0处,将索引p+1处的字节复制到索引1处,以此类推,知道将索引limit()-1处的字节复制到索引n=limit()-1-p处。然后将缓冲区的位置设置为n+1,并将其限制标记设置为其容量。如果已经定义了标记,则丢弃。将缓冲区的位置设置为复制的字节数,而不是0,以便调用此方法后可以紧接着调用另一个相对put方法。
压缩compact的执行过程如图:
比较缓冲区的内容有两种方法:equals()和compareTo()。这两办法还是有使用细节上的区别的,先来看一下ByteBuffer的equals的源码:
public boolean equals(Object ob) {
//判断是不是自身,是自身则返回true
if (this == ob)
return true;
//判断是不是ByteBuffer类的实例,如果不是返回false
if (!(ob instanceof ByteBuffer))
return false;
ByteBuffer that = (ByteBuffer)ob;
//判断remaining()的值是否一样,不一样则返回false
if (this.remaining() != that.remaining())
return false;
int p = this.position();
//判断两个缓冲区的position和limit之间的字节是否相同,只要有一个不相同则返回false
for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
if (!equals(this.get(i), that.get(j)))
return false;
return true;
}
所以说通过equals方法比较的是position和limit之间的内容是否完全一样,即使capacity不一样也没关系。
compareTo(ByteBuffer that)方法的作用:将此缓冲区和另外一个缓冲区进行比较。比较两个缓冲区的方法是按字典顺序比较他们剩余的的元素序列,而不考虑每个序列在其对应缓冲区中的起始位置。
public int compareTo(ByteBuffer that) {
int n = this.position() + Math.min(this.remaining(), that.remaining());
for (int i = this.position(), j = that.position(); i < n; i++, j++) {
int cmp = compare(this.get(i), that.get(j));
if (cmp != 0)
return cmp;
}
return this.remaining() - that.remaining();
}
从compareTo(ByteBuffer that)方法的源代码中可以分析出运算的3个主要逻辑。
通过源码来看,两个缓冲区的capacity可以不一样,这个特性和equals相同。
ByteBuffer duplicate()方法的作用:创建共享此缓冲区内容的新的字节缓冲区。新的缓冲区的内容为此缓冲区的内容,此缓冲区的内容更改在新缓冲区的是可见的,反之亦然。在创建新的缓冲区时,容量、限制、位置、标记的值与此缓冲区一致,但是这两个缓冲区的容量、限制、位置、标记的值相互独立。当且仅当此缓冲区为直接缓冲区时,新缓冲区才为直接缓冲区。当且仅当此缓冲区为只读缓冲区,新缓冲区才为只读缓冲区。
下面示例演示了duplicate()方法与slice()方法的区别
public class DuplicateDemo {
public static void main(String[] args) {
byte[] array1 = {1,2,3,4,5};
ByteBuffer buffer1 = ByteBuffer.wrap(array1);
buffer1.position(2);
System.out.println("buffer1 capacity = " + buffer1.capacity() +
" limit="+buffer1.limit()+" position="+buffer1.position());
ByteBuffer slice = buffer1.slice();
ByteBuffer duplicate = buffer1.duplicate();
System.out.println("slice capacity = " + slice.capacity() +
" limit="+slice.limit()+" position="+slice.position());
System.out.println("duplicate capacity = " + duplicate.capacity() +
" limit="+duplicate.limit()+" position="+duplicate.position());
slice.position(0);
for (int i = slice.position(); i < slice.limit(); i++) {
System.out.println(slice.get(i)+" ");
}
System.out.println();
duplicate.position(0);
for (int i = duplicate.position(); i < duplicate.limit(); i++) {
System.out.println(duplicate.get(i)+" ");
}
}
}
一旦创建了缓冲区,则其容量capacity就不能被改变。如果想要对缓冲区的容量进行扩展,就要进行相应的处理
public class ExtendsSize {
/**
* 缓冲区扩容
* @param buffer
* @param size
* @return
*/
public static ByteBuffer extendssize(ByteBuffer buffer , int size){
ByteBuffer allocate = ByteBuffer.allocate(buffer.capacity() + size);
allocate.put(buffer);
return allocate;
}
public static void main(String[] args) {
byte[] array = {1,2,3,4,5,6};
ByteBuffer wrap = ByteBuffer.wrap(array);
ByteBuffer extendssize = extendssize(wrap, 3);
System.out.println(Arrays.toString(extendssize.array()));
}
}