NIO相比普通IO提供了功能更为强大、处理数据更快的解决方案。
常用于高性能服务器上。NIO实现高性能处理的原理是使用较少的线程来处理更多的任务
常规io使用的byte[]、char[]进行封装,而NIO采用ByteBuffer类来操作数据,再结合
针对File或socket技术的channel,采用同步非阻塞技术来实现高性能处理,而Netty
正是采用ByteBuffer(缓冲区)、Channel(通道)、Selector(选择器)进行封装的。
因此我们需要先了解NIO相关的知识。
NIO相关知识:首先来了解ByteBuffer和CharBuffer,此时需要了解父类Buffer.
因此需要了解ByteBuffer的底层是否是基于数组实现的?可以看到如果是直接allocate,则不是,而是将元素的信息存入内存中,此时不经过数组,而allocate,则会存入到数组中。同时对于flip翻转有什么作用?我们是否可以自己实现翻转行为?答案是肯定的,同时将上界调为当前位置的大小,通过改变当前位置为0,即可实现,因为翻转的本质是为了实现对数据的获取,将数据进行取出。了解标记mark是什么,了解clear什么,了解hasRemaining()是什么,什么是rewind?什么是偏移量ArrayOffset?缓冲区的标记是一个索引,在调用reset()方法时,会将缓冲区的postion位置重置为该索引。同时mark不是必须的。如果将postion或limit调整为小于该mark值时,该mark会被丢弃,丢弃后mark的值为-1。如果未定义mark,调用reset()方法时会抛出InvalidMarkException异常。clear的作用是还原缓冲区到初始的状态,包含将位置设置为0,将限制设置为容量,并丢弃标记,即一切为默认。从源码中,我们看到,当前位置为0,上界等于容量,同时mark=-1。hasRemaing是判断当前位置到限制之间是否有元素,而remaing则是计算它们之间还有多少个元素。可以看到一开始时,字节数组是没有偏移量的,当时如果将postion设置为大于1的数时,也即当前位置大于0时,此时就会出现偏移量。因为偏移量是相对的。
首先其来自于java.nio下的Buffer:
public abstract class Buffer {
static final int SPLITERATOR_CHARACTERISTICS =
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
// 值的关系: mark <= position <= limit <= capacity
//也即标记<=位置<=限制<=容量,如果位置、限制<标记,则该标记会被丢弃,
//如果未定义mark,则reset时会被抛弃
//标记
private int mark = -1;
//位置 不能为负数
private int position = 0
//限制 不能为负数
private int limit;
//容量 不能为负数
private int capacity;
//地址,偏移量
long address;
//构造方法:在检查完不变量后创建一个新的带标记、位置、限制、容量的buffer
Buffer(int mark, int pos, int lim, int cap) {
//检查cap、mark是否<0,
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
this.mark = mark;
}
}
//容量
public final int capacity() {
return capacity;
}
//当前位置
public final int position() {
return position;
}
public final Buffer position(int newPosition) {
if ((newPosition > limit) || (newPosition < 0))
throw new IllegalArgumentException();
position = newPosition;
//对mark进行判断,从而是不是-1
if (mark > position) mark = -1;
return this;
}
public final int limit() {
return limit;
}
public final Buffer limit(int newLimit) {
if ((newLimit > capacity) || (newLimit < 0))
throw new IllegalArgumentException();
limit = newLimit;
if (position > limit) position = limit;
if (mark > limit) mark = -1;
return this;
}
//标记
public final Buffer mark() {
mark = position;
return this;
}
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
//清除,此时当前位置为0,同时清除标记,同时将上界变成了容量
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
//翻转
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
//重绕此缓冲区,将位置设置为0并丢弃标记
//可以看到将当前位置变成了0
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
//返回当前元素与limit之间的元素的数量
public final int remaining() {
return limit - position;
}
//当前位置是否小于上界,也即还是否有元素
public final boolean hasRemaining() {
return position < limit;
}
public abstract boolean isReadOnly();
//是否是数组
public abstract boolean hasArray();
public abstract Object array();
//数组偏移量
public abstract int arrayOffset();
public abstract boolean isDirect();
final int nextGetIndex() { // package-private
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
final int nextGetIndex(int nb) { // package-private
if (limit - position < nb)
throw new BufferUnderflowException();
int p = position;
position += nb;
return p;
}
final int nextPutIndex() { // package-private
if (position >= limit)
throw new BufferOverflowException();
return position++;
}
final int nextPutIndex(int nb) { // package-private
if (limit - position < nb)
throw new BufferOverflowException();
int p = position;
position += nb;
return p;
}
final int checkIndex(int i) { // package-private
if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException();
return i;
}
//检查索引
final int checkIndex(int i, int nb) { // package-private
if ((i < 0) || (nb > limit - i))
throw new IndexOutOfBoundsException();
return i;
}
final int markValue() { // package-private
return mark;
}
final void truncate() { // package-private
mark = -1;
position = 0;
limit = 0;
capacity = 0;
}
final void discardMark() { // package-private
mark = -1;
}
//检查边界:偏移量、长度、大小
static void checkBounds(int off, int len, int size) { // package-private
if ((off | len | (off + len) | (size - (off + len))) < 0)
throw new IndexOutOfBoundsException();
}
}
从包中,我们可以看到Buffer的子类抽象类包含的信息7个子类,常用的CharBuffer、ByteBuffer:
public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>
public abstract class CharBuffer extends Buffer implements Comparable<CharBuffer>, Appendable, CharSequence, Readable
public abstract class DoubleBuffer extends Buffer implements Comparable<DoubleBuffer>
public abstract class FloatBuffer extends Buffer implements Comparable<FloatBuffer>
public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer>
public abstract class LongBuffer extends Buffer implements Comparable<LongBuffer>
public abstract class ShortBuffer extends Buffer implements Comparable<ShortBuffer>
但是我们可以看到其7个子类也是抽象的,不能直接new实例化。那我们如何创建这些类的对象呢?使用的方式将上面7种数据类型的数组包装进缓冲区中,此时需要借助静态方法wrap()进行实现。wrap()方法的作用是将数组放入缓冲区中,来构建存储不同数据类型的缓冲区。比如byteBuffer
public static ByteBuffer wrap(byte[] array) {
//将byte包装成byteBuffer,包装的过程中传入的偏移量为0,同时长度为字节数组的长度
return wrap(array, 0, array.length);
}
public static ByteBuffer wrap(byte[] array, int offset, int length){
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}
//堆字节数组:本质是一个字节数组byte[]
HeapByteBuffer(byte[] buf, int off, int len) {
//从这里我们可以看到标记值为-1
super(-1, off, off + len, buf.length, buf, 0);
}
ByteBuffer(int mark, int pos, int lim, int cap,byte[] hb, int offset){
super(mark, pos, lim, cap);
this.hb = hb;
this.offset = offset;
}
Buffer(int mark, int pos, int lim, int cap) {
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
this.mark = mark;
}
}
下面我们来了解相关特性:
/**
* 进行nio测试:可以看到ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer
* 是抽象类,而wrap()就相当于创建这些缓冲区的工厂方法,都会经过wrap(array, 0, array.length)方法,比如ByteBuffer会经过
* //可以看到hb是一个字节数组
* ByteBuffer(int mark, int pos, int lim, int cap,byte[] hb, int offset) {
* //调用buffer的构造方法
* super(mark, pos, lim, cap);
* //仅堆缓冲区为非null
* this.hb = hb;
* //偏移量
* this.offset = offset;
* }
* <p>
* ByteBuffer类缓冲区的技术原理就是使用byte[]数组进行数据保存
* 缓冲区存储的数据还是存储在byte[]字节数组中。使用缓冲区与使用byte[]字节数组相比:
* 优点在于缓冲区将存储数据的byte[]字节数组内容与相关的信息整合在1个Buffer类中,将
* 数据与缓冲区中的信息进行了整合,并进行了封装,这样便于得到相关的信息和处理数据
*/
public class NIOTest {
public static void main(String[] args) {
byte[] byteArray = new byte[]{1, 2, 3};
short[] shortArray = new short[]{1, 2, 3, 4};
int[] intArray = new int[]{1, 2, 3, 4, 5};
long[] longArray = new long[]{1, 2, 3, 4, 5, 6, 7};
float[] floatArray = new float[]{1, 2, 3, 4, 5, 6, 7};
double[] doubleArray = new double[]{1, 2, 3, 4, 5, 6, 7, 8};
char[] charArray = new char[]{'a', 'b', 'c', 'd'};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
ShortBuffer shortBuffer = ShortBuffer.wrap(shortArray);
IntBuffer intBuffer = IntBuffer.wrap(intArray);
LongBuffer longBuffer = LongBuffer.wrap(longArray);
FloatBuffer floatBuffer = FloatBuffer.wrap(floatArray);
DoubleBuffer doubleBuffer = DoubleBuffer.wrap(doubleArray);
CharBuffer charBuffer = CharBuffer.wrap(charArray);
/**
* 返回结果:
* byteBuffer=java.nio.HeapByteBuffer
* shortBuffer=java.nio.HeapShortBuffer
* intBuffer=java.nio.HeapIntBuffer
* longBuffer=java.nio.HeapLongBuffer
* floatBuffer=java.nio.HeapFloatBuffer
* doubleBuffer=java.nio.HeapDoubleBuffer
* charBuffer=java.nio.HeapCharBuffer
*/
System.out.println("byteBuffer=" + byteBuffer.getClass().getName());
System.out.println("shortBuffer=" + shortBuffer.getClass().getName());
System.out.println("intBuffer=" + intBuffer.getClass().getName());
System.out.println("longBuffer=" + longBuffer.getClass().getName());
System.out.println("floatBuffer=" + floatBuffer.getClass().getName());
System.out.println("doubleBuffer=" + doubleBuffer.getClass().getName());
System.out.println("charBuffer=" + charBuffer.getClass().getName());
System.out.println("======================================");
System.out.println("byteBufffer.capacity=" + byteBuffer.capacity());
System.out.println("shortBuffer.capacity=" + shortBuffer.capacity());
System.out.println("intBuffer.capacity=" + intBuffer.capacity());
System.out.println("longBuffer.capacity=" + longBuffer.capacity());
System.out.println("floatBuffer.capacity=" + floatBuffer.capacity());
System.out.println("doubleBuffer.capacity=" + doubleBuffer.capacity());
System.out.println("charBuffer.capacity=" + charBuffer.capacity());
}
}
运行结果:
byteBuffer=java.nio.HeapByteBuffer
shortBuffer=java.nio.HeapShortBuffer
intBuffer=java.nio.HeapIntBuffer
longBuffer=java.nio.HeapLongBuffer
floatBuffer=java.nio.HeapFloatBuffer
doubleBuffer=java.nio.HeapDoubleBuffer
charBuffer=java.nio.HeapCharBuffer
======================================
byteBufffer.capacity=3
shortBuffer.capacity=4
intBuffer.capacity=5
longBuffer.capacity=7
floatBuffer.capacity=7
doubleBuffer.capacity=8
charBuffer.capacity=4
可以看到它会调用包装方法,进行包装也即上面所看到的wrap方法。最终是一个和堆有关的buffer的字节数组。
/**
* 进行limit和position使用
*/
public class NIOTest2 {
public static void main(String[] args) {
char[] charArray = new char[]{'a', 'b', 'c', 'd', 'd', 'e', 'f', 'g'};
CharBuffer buffer = CharBuffer.wrap(charArray);
System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit());
buffer.limit(4);
//buffer.position(2);
System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit());
buffer.put(0, '0');
buffer.put(1, 'h');
buffer.put(2, '3');
buffer.put(3, 'i');
//会在这里抛异常,因为指定了现在4个数据
buffer.put(4, 'j');
buffer.put(5, 'k');
}
}
运行结果:
Exception in thread "main" java.lang.IndexOutOfBoundsException
at java.nio.Buffer.checkIndex(Buffer.java:540)
at java.nio.HeapCharBuffer.put(HeapCharBuffer.java:178)
at com.study.nio.NIOTest2.main(NIOTest2.java:21)
A capacity()=8 limit()=8
B capacity()=8 limit()=4
从运行的结果中,我们可以看到原来包装的buffer的容量是8,同时上界是8。而设置上界为4,此时当添加数据到第四个位置时,会抛异常。
/**
* 进行limit和position、remaining的使用
*/
public class NIOTest3 {
public static void main(String[] args) {
char[] charArray = new char[]{'a', 'b', 'c', 'd', 'd', 'e', 'f', 'g'};
CharBuffer buffer = CharBuffer.wrap(charArray);
System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
buffer.limit(5);
//buffer的下一个位置是2号角标的位置
buffer.position(2);
//remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postion
System.out.println("remaining()=" + buffer.remaining());
System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
buffer.put('i'); //第三个数据c会变成i
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i] + " ");
}
}
}
运行结果:
A capacity()=8 limit()=8 position()=0
remaining()=3
B capacity()=8 limit()=5 position()=2
a b i d d e f g
/**
* 使用buffer mark()方法处理标记
* mark()的作用:在此缓冲区的位置设置标记,
* 缓冲去的标记是一个索引,在调用reset()方法时,
* 会将缓冲区的position位置重置为索引位置。
* <p>
* 如果定义了mark,则在将postion或limit调整为小于该mark的值时,该mark会被丢弃,丢弃后mark的值为-1
* 如果未定义mark,调用reset会抛出InvalidMarkException异常
* 同时可以看到默认是不开启直接缓冲区的,需要手动设置,此时在jvm和硬盘之间可以少了一个中间缓冲区,提高程序运行的效率
*/
public class NIOTest4 {
public static void main(String[] args) {
byte[] byteArray = new byte[]{'a', 'b', 'c', 'd', 'd', 'e', 'f'};
ByteBuffer buffer = ByteBuffer.wrap(byteArray);
System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
buffer.limit(5);
//buffer的下一个位置是2号角标的位置
buffer.position(2);
//在位置2设置mark
buffer.mark();
System.out.println("remaining()=" + buffer.remaining());
System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
buffer.position(3);
//位置重置,postion的位置会回到mark的位置
buffer.reset();
//remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postion
System.out.println("remaining()=" + buffer.remaining());
System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
//可以看到不是直接缓冲区
System.out.println(buffer.isDirect());
ByteBuffer byBuffer = ByteBuffer.allocate(100);
System.out.println("默认关闭,此时调用的不是直接缓冲区:" + byBuffer.isDirect());
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
System.out.println("此时调用的是直接缓冲区:" + byteBuffer.isDirect());
}
}
运行结果:
A capacity()=7 limit()=7 position()=0
remaining()=3
B capacity()=7 limit()=5 position()=2
remaining()=3
B capacity()=7 limit()=5 position()=2
false
默认关闭,此时调用的不是直接缓冲区:false
此时调用的是直接缓冲区:true
可以看到如果当前位置为2,上界为5时,如果设置为mark,则此时剩下的元素是3。同时可以看到如果是直接缓冲区,则调用的是内存,否则不是直接缓冲区。
/**
* 对缓存区进行反转 flip()方法:原理是首先将闲置设置为当前位置,然后将
* 位置设置为0.如果定义了标记,则丢弃该标记。
* public Buffer flip(){
* limit = postion;
* postion = 0;
* mark = -1;
* return this;
* }
*/
public class NIOTest5 {
public static void main(String[] args) {
byte[] byteArray = new byte[]{'a', 'b', 'c', 'd', 'd', 'e'};
ByteBuffer buffer = ByteBuffer.wrap(byteArray);
System.out.println("A capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
//buffer的下一个位置是2号角标的位置
buffer.position(2);
//在位置2设置mark
buffer.mark();
buffer.flip();
System.out.println("remaining()=" + buffer.remaining());
System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
//位置重置,postion的位置会回到mark的位置
try {
buffer.reset();
} catch (Exception e) {
System.out.println("buffer的mark丢失了。。。");
}
//remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postion
System.out.println("remaining()=" + buffer.remaining());
System.out.println("B capacity()=" + buffer.capacity() + " limit()=" + buffer.limit() + " position()=" + buffer.position());
}
}
运行结果:
A capacity()=6 limit()=6 position()=0
remaining()=2
B capacity()=6 limit()=2 position()=0
buffer的mark丢失了。。。
remaining()=2
B capacity()=6 limit()=2 position()=0
flip操作会将原来的位置翻转到初始位置。从源码中可以看到其会将postion设置为0。
/**
* 测试postion和limit的使用,模仿flip行为
*/
public class NIOTest6 {
public static void main(String[] args) {
//创建一个CharBuffer,容量为20
CharBuffer charBuffer = CharBuffer.allocate(20);
//查看当前位置和限制大小,你可以理解为上界
System.out.println("A postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//写入字符
charBuffer.put("一个前行在路上的行路人");
System.out.println("B postion =" + charBuffer.position() + "limit=" + charBuffer.limit());
//还原位置到0
charBuffer.position(0);
System.out.println("C postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//写入字符
charBuffer.put("学无止境,你在前行");
System.out.println("D postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//还原缓冲区的状态
charBuffer.clear();
System.out.println("E postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//继续写入
charBuffer.put("我不前行的时候,你也在前行");
charBuffer.append("我要更努力");
charBuffer.limit(charBuffer.position());
charBuffer.position(0);
for (int i = 0; i < charBuffer.limit(); i++) {
System.out.print(charBuffer.get());
}
System.out.println(" ");
}
}
运行结果:
A postion = 0 limit=20
B postion =11 limit=20
C postion = 0 limit=20
D postion = 9 limit=20
E postion = 0 limit=20
我不前行的时候,你也在前行我要更努力
从上面的运行结果看,我们成功模仿了flip的翻转行为。
/**
* 使用flip解决NIOTest6的行为
*/
public class NIOTest7 {
public static void main(String[] args) {
//创建一个CharBuffer,容量为20
CharBuffer charBuffer = CharBuffer.allocate(20);
//查看当前位置和限制大小,你可以理解为上界
System.out.println("A postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//写入字符
charBuffer.put("一个前行在路上的行路人");
System.out.println("B postion =" + charBuffer.position() + "limit=" + charBuffer.limit());
//还原位置到0
charBuffer.position(0);
System.out.println("C postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//写入字符
charBuffer.put("学无止境,你在前行");
System.out.println("D postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//还原缓冲区的状态
charBuffer.clear();
System.out.println("E postion = " + charBuffer.position() + "limit=" + charBuffer.limit());
//继续写入
charBuffer.put("我不前行的时候,你也在前行");
charBuffer.append("我要更努力");
//charBuffer.limit(charBuffer.position());
//charBuffer.position(0);
charBuffer.flip();
for (int i = 0; i < charBuffer.limit(); i++) {
System.out.print(charBuffer.get());
}
System.out.println(" ");
}
}
也即:
charBuffer.limit(charBuffer.position());
charBuffer.position(0);
=> charBuffer.flip();
/**
* 查看缓冲区底层实现是否是数组实现的
*/
public class NIOTest8 {
public static void main(String[] args) {
allocateDemo();
allocateDirectDemo();
}
private static void allocateDemo() {
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
//返回true,说明底层是数组实现的,说明数据存储在数组中
System.out.println("缓冲区的底层基于数组实现:" + byteBuffer.hasArray());
}
private static void allocateDirectDemo() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
//返回true,说明底层是数组实现的,而返回false,说明数据并没有直接存储在数组中,而是直接存储在内存中
System.out.println("直接缓冲区的底层基于数组实现:" + byteBuffer.hasArray());
}
}
运行结果:
缓冲区的底层基于数组实现:true
直接缓冲区的底层基于数组实现:false
/**
* 判断当前位置和限制之间是否有剩余元素
* 进行迭代
*/
public class NIOTest9 {
public static void main(String[] args) {
hasRemain();
hasElementGet();
}
//测试还剩下的元素
private static void hasRemain() {
byte[] byteArray = new byte[]{1, 2, 3, 4};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
byteBuffer.limit(4);
byteBuffer.position(2);
System.out.println("byteBuffer.hasRemaining=" + byteBuffer.hasRemaining() + " " + "byteBuffer.remaining=" + byteBuffer.remaining());
}
//使用剩下的特性,进行迭代
private static void hasElementGet() {
byte[] byteArray = new byte[]{1, 2, 3, 4, 5};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
int remaining = byteBuffer.remaining();
for (int i = 0; i < remaining; i++) {
System.out.print(byteBuffer.get() + "");
}
System.out.println("");
byteBuffer.clear();
while (byteBuffer.hasRemaining()) {
System.out.print(byteBuffer.get() + "");
}
System.out.println("");
byteBuffer.clear();
for (; byteBuffer.hasRemaining() == true; ) {
System.out.print(byteBuffer.get() + "");
}
System.out.println("");
}
}
运行结果:
byteBuffer.hasRemaining=true byteBuffer.remaining=2
12345
12345
12345
/**
* 对于flip、clear、rewind之间的区别:
* remind:标记清除,位置postion值归0,limit不变
* public final Buffer rewind(){
* postion = 0;
* mark = -1;
* return this;
* }
* <p>
* clear:清除缓冲区,将位置设置为0,将限制设置为容量,并丢弃标记
* public final Buffer clear(){
* postion = 0;
* limit = capacity;
* mark = -1;
* return -1;
* }
* <p>
* flip:反转缓冲区,首先将限制设置为当前位置,然后将位置设置为0.
* public final Buffer flip(){
* limit = postion;
* postion = 0;
* mark = -1;
* return this;
* }
* <p>
* 三者的侧重点:
* rewind:侧重在重新,在重新读取、重新写入时使用
* clear:侧重还原一切状态
* flip:侧重在substring截取
*/
public class NIOTest10 {
public static void main(String[] args) {
byte[] byteArray = new byte[]{1, 2, 3, 4};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
System.out.println("capacity=" + byteBuffer.capacity() + " " + "limit=" + byteBuffer.limit() + " " + "postion=" + byteBuffer.position());
byteBuffer.position(1);
byteBuffer.limit(3);
byteBuffer.mark();
System.out.println("capacity=" + byteBuffer.capacity() + " " + "limit=" + byteBuffer.limit() + " " + "postion=" + byteBuffer.position());
byteBuffer.rewind();
System.out.println("capacity=" + byteBuffer.capacity() + " " + "limit=" + byteBuffer.limit() + " " + "postion=" + byteBuffer.position());
}
}
运行结果:
capacity=4 limit=4 postion=0
capacity=4 limit=3 postion=1
capacity=4 limit=3 postion=0
/**
* 获取偏移量 ArrayOffset
* final int arrayOffset():返回此缓冲区的底层实现数组中的第一个缓冲区元素的偏移量
* public final int arrayOffset(){
* if(hb==null)
* throw new UnsupportedOperationException();
* if(isReadOnly)
* throw new ReadOnlyBufferException();
* return offset;
* }
*/
public class NIOTest11 {
public static void main(String[] args) {
getArrayOffsetZero();
getArrayOffset();
}
//测试结果永远都为0的情况
private static void getArrayOffsetZero() {
byte[] byteArray = new byte[]{1,2,3,4,5,6};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
System.out.println("byteBuffer.arrayOffset ="+byteBuffer.arrayOffset());
}
//测试不为0的情况,偏移是相对而言的
private static void getArrayOffset() {
byte[] byteArray = new byte[]{1,2,3,4,5,6};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
byteBuffer.position(5);
ByteBuffer byteBuffer1 = byteBuffer.slice();
System.out.println("byteBuffer.arrayOffset ="+byteBuffer1.arrayOffset());
}
}
运行结果:
byteBuffer.arrayOffset =0
byteBuffer.arrayOffset =5
/**
* 使用List.toArray(T[])
*/
public class NIOTest12 {
public static void main(String[] args) {
ByteBuffer buffer1 = ByteBuffer.wrap(new byte[]{'1','2','3','4'});
ByteBuffer buffer2 = ByteBuffer.wrap(new byte[]{'c','d','t','v'});
ByteBuffer buffer3 = ByteBuffer.wrap(new byte[]{'x','m','a','n'});
List<ByteBuffer> list = new ArrayList<>();
list.add(buffer1);
list.add(buffer2);
list.add(buffer3);
ByteBuffer[] byteBufferArray = new ByteBuffer[list.size()];
list.toArray(byteBufferArray);
System.out.println(byteBufferArray.length);
for (int i = 0; i<byteBufferArray.length;i++){
ByteBuffer eachByteBuffer = byteBufferArray[i];
while (eachByteBuffer.hasRemaining()){
System.out.print((char)eachByteBuffer.get());
}
System.out.println();
}
}
}
运行结果:
3
1234
cdtv
xman
/**
* 进行测试,包装wrap数据的处理
*/
public class NIOTest13 {
public static void main(String[] args) {
byte[] byteArray = new byte[]{1, 2, 3, 4, 5, 56};
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
ByteBuffer byteBuffer1 = ByteBuffer.wrap(byteArray, 2, 4);
System.out.println("byteBuffer capacity=" + byteBuffer.capacity() + " " + "postion=" + byteBuffer.position());
System.out.println();
System.out.println("byteBuffer1 capacity=" + byteBuffer1.capacity() + " " + "postion=" + byteBuffer1.position());
}
}
运行结果:
byteBuffer capacity=6 postion=0
byteBuffer1 capacity=6 postion=2
了解完Buffer之后,我们就可以接着了解ByteBuffer和CharBuffer了。