public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
mark方法和rest方法联合使用可实现从指定位置的重读。
7)rewind:从头开始重读
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
ByteBuffer对象使用时又很多需要注意的地方,自认为这个API设计的不是很友好。比如一定不能连续两次调用flip和compact方法,flip方法调用以后不能再调用put方法,等等。要避免这些错误,只能在使用ByteBuffer前弄清楚当前缓冲区中0到position-1以及position到limit中数据表示的含义,这才是避免bug的根本办法。
从上面的介绍中我们可以看出,ByteBuffer对象既可以读,也可以写。除非我们能保证在读操作一次性使用完ByteBuffer对象中的所有数据,并且保证写入ByteBuffer对象向中的内容全部写入完成,否则同时用于读写的ByteBuffer对象会造成数据的混乱和错误。一般来说,我们都会创建两个ByteBuffer对象向,一个用于接收数据,另一个用于发送数据。
1.3其它方法
ByteBuffer是面向字节的,为方便基本数据类型的读取,ByteBuffer中还提供getInt,putInt,getFloat,putFloat等方法,这些方法方便我们在缓冲区存取单个基本数据类型。如果需要从基本数据类型数组中写入到ByteBuffer中,或者从ByteBuffer中读取到基本数据类型的数组中,那么我们可以通过已创建好的ByteBuffer对象的asXxxBuffer方法创建基本数据类型的Buffer。
abstract CharBuffer
asCharBuffer()
abstract DoubleBuffer
asDoubleBuffer()
abstract FloatBuffer
asFloatBuffer()
abstract IntBuffer
asIntBuffer()
abstract LongBuffer
asLongBuffer()
假设有如下代码
IntBuffer intBufferObj = byteBufferObj.asIntBuffer();
此时intBufferObj和byteBufferObj对象共享底层���数组。但是比较坑爹的是两个buffer的position,limit是独立的,这样极易产生bug,需要引起我们注意。
1.4 ByteBuffer的编码和解码
数据传输中我们使用的是ByteBuffer对象作为缓冲区,如果在通道两端我们通信的内容是文本数据,这就涉及到ByteBuffer与CharBuffer的转换。我们可以使用Charset类实现这个转换的功能。
1)解码示例
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
byteBuffer.put(new byte[]{-26, -120, -111, -25, -120, -79, -28, -67, -96});
byteBuffer.flip();
/*对获取utf8的编解码器*/
Charset utf8 = Charset.forName("UTF-8");
CharBuffer charBuffer = utf8.decode(byteBuffer);/*对bytebuffer中的内容解码*/
/*array()返回的就是内部的数组引用,编码以后的有效长度是0~limit*/
char[] charArr = Arrays.copyOf(charBuffer.array(), charBuffer.limit());
System.out.println(charArr); /*运行结果:我爱你*/
2)编码示例
CharBuffer charBuffer = CharBuffer.allocate(128);
charBuffer.append("我爱你");
charBuffer.flip();
/*对获取utf8的编解码器*/
Charset utf8 = Charset.forName("UTF-8");
ByteBuffer byteBuffer = utf8.encode(charBuffer); /*对charbuffer中的内容解码*/
/*array()返回的就是内部的数组引用,编码以后的有效长度是0~limit*/
byte[] bytes = Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
System.out.println(Arrays.toString(bytes));
/*运行结果:[-26, -120, -111, -25, -120, -79, -28, -67, -96] */
我们还可以通过代码中的utf8编解码器分别获取编码器对象和解码器对象
CharsetEncoder utf8Encoder = utf8.newEncoder();
CharsetDecoder utf8Decoder = utf8.newDecoder();
然后通过下面编码器和解码器提供的方法进行编解码,其中一些方法可以使ByteBuffer和CharBuffer对象循环使用,不必每次都产生一个新的对象。
解码器方法
CharBuffer
decode(ByteBuffer in)
Convenience method that decodes the remaining content of a single input byte buffer into a newly-allocated character buffer.
CoderResult
decode(ByteBuffer in, CharBuffer out, boolean endOfInput)
Decodes as many bytes as possible from the given input buffer, writing the results to the given output buffer.
protected abstract CoderResult
decodeLoop(ByteBuffer in, CharBuffer out)
Decodes one or more bytes into one or more characters.
编码器方法
ByteBuffer
encode(CharBuffer in)
Convenience method that encodes the remaining content of a single input character buffer into a newly-allocated byte buffer.
CoderResult
encode(CharBuffer in, ByteBuffer out, boolean endOfInput)
Encodes as many characters as possible from the given input buffer, writing the results to the given output buffer.
protected abstract CoderResult
encodeLoop(CharBuffer in, ByteBuffer out)
Encodes one or more characters into one or more bytes.
注意encode和decode方法都会改变源buffer中的position的位置,这点也是容易产生bug的方法。
2. Channel
针对四种不同的应用场景,有四种不同类型的Channel对象。
类型
应用场景
是否阻塞
FileChannel
文件
阻塞
DatagramChannel
UDP协议
阻塞或非阻塞
SocketChannel
TCP协议
阻塞或非阻塞
ServerSocketChannel
用于TCP服务器端的监听和链接
阻塞或非阻塞