NIO学习一

简介: NIO相比普通IO提供了功能更为强大、处理数据更快的解决方案。常用于高性能服务器上。NIO实现高性能处理的原理是使用较少的线程来处理更多的任务常规io使用的byte[]、char[]进行封装,而NIO采用ByteBuffer类来操作数据,再结合针对File或socket技术的channel,采用同步非阻塞技术来实现高性能处理,而Netty正是采用ByteBuffer(缓冲区)、Channel(通道)、Selector(选择器)进行封装的。因此我们需要先了解NIO相关的知识。
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:

publicabstractclassBuffer {
staticfinalintSPLITERATOR_CHARACTERISTICS=Spliterator.SIZED|Spliterator.SUBSIZED|Spliterator.ORDERED;
// 值的关系:mark <= position <= limit <= capacity//也即标记<=位置<=限制<=容量,如果位置、限制<标记,则该标记会被丢弃,//如果未定义mark,则reset时会被抛弃//标记privateintmark=-1;
//位置  不能为负数privateintposition=0//限制 不能为负数   privateintlimit;
//容量 不能为负数privateintcapacity;
//地址,偏移量longaddress;
//构造方法:在检查完不变量后创建一个新的带标记、位置、限制、容量的bufferBuffer(intmark, intpos, intlim, intcap) {
//检查cap、mark是否<0,if (cap<0)
thrownewIllegalArgumentException("Negative capacity: "+cap);
this.capacity=cap;
limit(lim);
position(pos);
if (mark>=0) {
if (mark>pos)
thrownewIllegalArgumentException("mark > position: ("+mark+" > "+pos+")");
this.mark=mark;
        }
    }
//容量publicfinalintcapacity() {
returncapacity;
    }
//当前位置publicfinalintposition() {
returnposition;
    }
publicfinalBufferposition(intnewPosition) {
if ((newPosition>limit) || (newPosition<0))
thrownewIllegalArgumentException();
position=newPosition;
//对mark进行判断,从而是不是-1if (mark>position) mark=-1;
returnthis;
    }
publicfinalintlimit() {
returnlimit;
    }
publicfinalBufferlimit(intnewLimit) {
if ((newLimit>capacity) || (newLimit<0))
thrownewIllegalArgumentException();
limit=newLimit;
if (position>limit) position=limit;
if (mark>limit) mark=-1;
returnthis;
    }
//标记publicfinalBuffermark() {
mark=position;
returnthis;
    }
publicfinalBufferreset() {
intm=mark;
if (m<0)
thrownewInvalidMarkException();
position=m;
returnthis;
    }
//清除,此时当前位置为0,同时清除标记,同时将上界变成了容量publicfinalBufferclear() {
position=0;
limit=capacity;
mark=-1;
returnthis;
    }
//翻转publicfinalBufferflip() {
limit=position;
position=0;
mark=-1;
returnthis;
    }
//重绕此缓冲区,将位置设置为0并丢弃标记//可以看到将当前位置变成了0publicfinalBufferrewind() {
position=0;
mark=-1;
returnthis;
    }
//返回当前元素与limit之间的元素的数量publicfinalintremaining() {
returnlimit-position;
    }
//当前位置是否小于上界,也即还是否有元素publicfinalbooleanhasRemaining() {
returnposition<limit;
    }
publicabstractbooleanisReadOnly();
//是否是数组publicabstractbooleanhasArray();
publicabstractObjectarray();
//数组偏移量publicabstractintarrayOffset();
publicabstractbooleanisDirect();
finalintnextGetIndex() {                          // package-privateif (position>=limit)
thrownewBufferUnderflowException();
returnposition++;
    }
finalintnextGetIndex(intnb) {                    // package-privateif (limit-position<nb)
thrownewBufferUnderflowException();
intp=position;
position+=nb;
returnp;
    }
finalintnextPutIndex() {                          // package-privateif (position>=limit)
thrownewBufferOverflowException();
returnposition++;
    }
finalintnextPutIndex(intnb) {                    // package-privateif (limit-position<nb)
thrownewBufferOverflowException();
intp=position;
position+=nb;
returnp;
    }
finalintcheckIndex(inti) {                       // package-privateif ((i<0) || (i>=limit))
thrownewIndexOutOfBoundsException();
returni;
    }
//检查索引finalintcheckIndex(inti, intnb) {               // package-privateif ((i<0) || (nb>limit-i))
thrownewIndexOutOfBoundsException();
returni;
    }
finalintmarkValue() {                             // package-privatereturnmark;
    }
finalvoidtruncate() {                             // package-privatemark=-1;
position=0;
limit=0;
capacity=0;
    }
finalvoiddiscardMark() {                          // package-privatemark=-1;
    }
//检查边界:偏移量、长度、大小staticvoidcheckBounds(intoff, intlen, intsize) { // package-privateif ((off|len| (off+len) | (size- (off+len))) <0)
thrownewIndexOutOfBoundsException();
    }
}

从包中,我们可以看到Buffer的子类抽象类包含的信息7个子类,常用的CharBuffer、ByteBuffer:

publicabstractclassByteBufferextendsBufferimplementsComparable<ByteBuffer>publicabstractclassCharBufferextendsBufferimplementsComparable<CharBuffer>, Appendable, CharSequence, ReadablepublicabstractclassDoubleBufferextendsBufferimplementsComparable<DoubleBuffer>publicabstractclassFloatBufferextendsBufferimplementsComparable<FloatBuffer>publicabstractclassIntBufferextendsBufferimplementsComparable<IntBuffer>publicabstractclassLongBufferextendsBufferimplementsComparable<LongBuffer>publicabstractclassShortBufferextendsBufferimplementsComparable<ShortBuffer>

但是我们可以看到其7个子类也是抽象的,不能直接new实例化。那我们如何创建这些类的对象呢?使用的方式将上面7种数据类型的数组包装进缓冲区中,此时需要借助静态方法wrap()进行实现。wrap()方法的作用是将数组放入缓冲区中,来构建存储不同数据类型的缓冲区。比如byteBuffer

publicstaticByteBufferwrap(byte[] array) {
//将byte包装成byteBuffer,包装的过程中传入的偏移量为0,同时长度为字节数组的长度returnwrap(array, 0, array.length);
 }
publicstaticByteBufferwrap(byte[] array, intoffset, intlength){
try {
returnnewHeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentExceptionx) {
thrownewIndexOutOfBoundsException();
        }
    }
//堆字节数组:本质是一个字节数组byte[]HeapByteBuffer(byte[] buf, intoff, intlen) { 
//从这里我们可以看到标记值为-1super(-1, off, off+len, buf.length, buf, 0);
}
ByteBuffer(intmark, intpos, intlim, intcap,byte[] hb, intoffset){
super(mark, pos, lim, cap);
this.hb=hb;
this.offset=offset;
}
Buffer(intmark, intpos, intlim, intcap) {
if (cap<0)
thrownewIllegalArgumentException("Negative capacity: "+cap);
this.capacity=cap;
limit(lim);
position(pos);
if (mark>=0) {
if (mark>pos)
thrownewIllegalArgumentException("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类中,将* 数据与缓冲区中的信息进行了整合,并进行了封装,这样便于得到相关的信息和处理数据*/publicclassNIOTest {
publicstaticvoidmain(String[] args) {
byte[] byteArray=newbyte[]{1, 2, 3};
short[] shortArray=newshort[]{1, 2, 3, 4};
int[] intArray=newint[]{1, 2, 3, 4, 5};
long[] longArray=newlong[]{1, 2, 3, 4, 5, 6, 7};
float[] floatArray=newfloat[]{1, 2, 3, 4, 5, 6, 7};
double[] doubleArray=newdouble[]{1, 2, 3, 4, 5, 6, 7, 8};
char[] charArray=newchar[]{'a', 'b', 'c', 'd'};
ByteBufferbyteBuffer=ByteBuffer.wrap(byteArray);
ShortBuffershortBuffer=ShortBuffer.wrap(shortArray);
IntBufferintBuffer=IntBuffer.wrap(intArray);
LongBufferlongBuffer=LongBuffer.wrap(longArray);
FloatBufferfloatBuffer=FloatBuffer.wrap(floatArray);
DoubleBufferdoubleBuffer=DoubleBuffer.wrap(doubleArray);
CharBuffercharBuffer=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.HeapByteBuffershortBuffer=java.nio.HeapShortBufferintBuffer=java.nio.HeapIntBufferlongBuffer=java.nio.HeapLongBufferfloatBuffer=java.nio.HeapFloatBufferdoubleBuffer=java.nio.HeapDoubleBuffercharBuffer=java.nio.HeapCharBuffer======================================byteBufffer.capacity=3shortBuffer.capacity=4intBuffer.capacity=5longBuffer.capacity=7floatBuffer.capacity=7doubleBuffer.capacity=8charBuffer.capacity=4

可以看到它会调用包装方法,进行包装也即上面所看到的wrap方法。最终是一个和堆有关的buffer的字节数组。

/*** 进行limit和position使用*/publicclassNIOTest2 {
publicstaticvoidmain(String[] args) {
char[] charArray=newchar[]{'a', 'b', 'c', 'd', 'd', 'e', 'f', 'g'};
CharBufferbuffer=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');
    }
}

运行结果:

Exceptioninthread"main"java.lang.IndexOutOfBoundsExceptionatjava.nio.Buffer.checkIndex(Buffer.java:540)
atjava.nio.HeapCharBuffer.put(HeapCharBuffer.java:178)
atcom.study.nio.NIOTest2.main(NIOTest2.java:21)
Acapacity()=8limit()=8Bcapacity()=8limit()=4

从运行的结果中,我们可以看到原来包装的buffer的容量是8,同时上界是8。而设置上界为4,此时当添加数据到第四个位置时,会抛异常。

/*** 进行limit和position、remaining的使用*/publicclassNIOTest3 {
publicstaticvoidmain(String[] args) {
char[] charArray=newchar[]{'a', 'b', 'c', 'd', 'd', 'e', 'f', 'g'};
CharBufferbuffer=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-postionSystem.out.println("remaining()="+buffer.remaining());
System.out.println("B capacity()="+buffer.capacity() +" limit()="+buffer.limit() +" position()="+buffer.position());
buffer.put('i'); //第三个数据c会变成ifor (inti=0; i<charArray.length; i++) {
System.out.println(charArray[i] +" ");
        }
    }
}

运行结果:

Acapacity()=8limit()=8position()=0remaining()=3Bcapacity()=8limit()=5position()=2abiddefg
/*** 使用buffer mark()方法处理标记* mark()的作用:在此缓冲区的位置设置标记,* 缓冲去的标记是一个索引,在调用reset()方法时,* 会将缓冲区的position位置重置为索引位置。* <p>* 如果定义了mark,则在将postion或limit调整为小于该mark的值时,该mark会被丢弃,丢弃后mark的值为-1* 如果未定义mark,调用reset会抛出InvalidMarkException异常* 同时可以看到默认是不开启直接缓冲区的,需要手动设置,此时在jvm和硬盘之间可以少了一个中间缓冲区,提高程序运行的效率*/publicclassNIOTest4 {
publicstaticvoidmain(String[] args) {
byte[] byteArray=newbyte[]{'a', 'b', 'c', 'd', 'd', 'e', 'f'};
ByteBufferbuffer=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设置markbuffer.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-postionSystem.out.println("remaining()="+buffer.remaining());
System.out.println("B capacity()="+buffer.capacity() +" limit()="+buffer.limit() +" position()="+buffer.position());
//可以看到不是直接缓冲区System.out.println(buffer.isDirect());
ByteBufferbyBuffer=ByteBuffer.allocate(100);
System.out.println("默认关闭,此时调用的不是直接缓冲区:"+byBuffer.isDirect());
ByteBufferbyteBuffer=ByteBuffer.allocateDirect(100);
System.out.println("此时调用的是直接缓冲区:"+byteBuffer.isDirect());
    }
}

运行结果:

Acapacity()=7limit()=7position()=0remaining()=3Bcapacity()=7limit()=5position()=2remaining()=3Bcapacity()=7limit()=5position()=2false默认关闭,此时调用的不是直接缓冲区:false此时调用的是直接缓冲区:true

可以看到如果当前位置为2,上界为5时,如果设置为mark,则此时剩下的元素是3。同时可以看到如果是直接缓冲区,则调用的是内存,否则不是直接缓冲区。

/*** 对缓存区进行反转 flip()方法:原理是首先将闲置设置为当前位置,然后将* 位置设置为0.如果定义了标记,则丢弃该标记。* public Buffer flip(){* limit = postion;* postion = 0;* mark = -1;* return this;* }*/publicclassNIOTest5 {
publicstaticvoidmain(String[] args) {
byte[] byteArray=newbyte[]{'a', 'b', 'c', 'd', 'd', 'e'};
ByteBufferbuffer=ByteBuffer.wrap(byteArray);
System.out.println("A capacity()="+buffer.capacity() +" limit()="+buffer.limit() +" position()="+buffer.position());
//buffer的下一个位置是2号角标的位置buffer.position(2);
//在位置2设置markbuffer.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 (Exceptione) {
System.out.println("buffer的mark丢失了。。。");
        }
//remaining()的作用:返回当前位置与limit之间的元素数,也即:remaining=limit-postionSystem.out.println("remaining()="+buffer.remaining());
System.out.println("B capacity()="+buffer.capacity() +" limit()="+buffer.limit() +" position()="+buffer.position());
    }
}

运行结果:

Acapacity()=6limit()=6position()=0remaining()=2Bcapacity()=6limit()=2position()=0buffer的mark丢失了。。。remaining()=2Bcapacity()=6limit()=2position()=0

flip操作会将原来的位置翻转到初始位置。从源码中可以看到其会将postion设置为0。

/*** 测试postion和limit的使用,模仿flip行为*/publicclassNIOTest6 {
publicstaticvoidmain(String[] args) {
//创建一个CharBuffer,容量为20CharBuffercharBuffer=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());
//还原位置到0charBuffer.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 (inti=0; i<charBuffer.limit(); i++) {
System.out.print(charBuffer.get());
        }
System.out.println(" ");
    }
}

运行结果:

Apostion=0limit=20Bpostion=11limit=20Cpostion=0limit=20Dpostion=9limit=20Epostion=0limit=20我不前行的时候,你也在前行我要更努力

从上面的运行结果看,我们成功模仿了flip的翻转行为。

/*** 使用flip解决NIOTest6的行为*/publicclassNIOTest7 {
publicstaticvoidmain(String[] args) {
//创建一个CharBuffer,容量为20CharBuffercharBuffer=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());
//还原位置到0charBuffer.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 (inti=0; i<charBuffer.limit(); i++) {
System.out.print(charBuffer.get());
        }
System.out.println(" ");
    }
}

也即:

charBuffer.limit(charBuffer.position());
charBuffer.position(0);
=>charBuffer.flip();
/*** 查看缓冲区底层实现是否是数组实现的*/publicclassNIOTest8 {
publicstaticvoidmain(String[] args) {
allocateDemo();
allocateDirectDemo();
    }
privatestaticvoidallocateDemo() {
ByteBufferbyteBuffer=ByteBuffer.allocate(100);
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
//返回true,说明底层是数组实现的,说明数据存储在数组中System.out.println("缓冲区的底层基于数组实现:"+byteBuffer.hasArray());
    }
privatestaticvoidallocateDirectDemo() {
ByteBufferbyteBuffer=ByteBuffer.allocateDirect(100);
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
//返回true,说明底层是数组实现的,而返回false,说明数据并没有直接存储在数组中,而是直接存储在内存中System.out.println("直接缓冲区的底层基于数组实现:"+byteBuffer.hasArray());
    }
}

运行结果:

缓冲区的底层基于数组实现:true直接缓冲区的底层基于数组实现:false
/*** 判断当前位置和限制之间是否有剩余元素* 进行迭代*/publicclassNIOTest9 {
publicstaticvoidmain(String[] args) {
hasRemain();
hasElementGet();
    }
//测试还剩下的元素privatestaticvoidhasRemain() {
byte[] byteArray=newbyte[]{1, 2, 3, 4};
ByteBufferbyteBuffer=ByteBuffer.wrap(byteArray);
byteBuffer.limit(4);
byteBuffer.position(2);
System.out.println("byteBuffer.hasRemaining="+byteBuffer.hasRemaining() +" "+"byteBuffer.remaining="+byteBuffer.remaining());
    }
//使用剩下的特性,进行迭代privatestaticvoidhasElementGet() {
byte[] byteArray=newbyte[]{1, 2, 3, 4, 5};
ByteBufferbyteBuffer=ByteBuffer.wrap(byteArray);
intremaining=byteBuffer.remaining();
for (inti=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=truebyteBuffer.remaining=2123451234512345
/*** 对于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截取*/publicclassNIOTest10 {
publicstaticvoidmain(String[] args) {
byte[] byteArray=newbyte[]{1, 2, 3, 4};
ByteBufferbyteBuffer=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=4limit=4postion=0capacity=4limit=3postion=1capacity=4limit=3postion=0
/*** 获取偏移量 ArrayOffset* final int arrayOffset():返回此缓冲区的底层实现数组中的第一个缓冲区元素的偏移量* public final int arrayOffset(){* if(hb==null)* throw new UnsupportedOperationException();* if(isReadOnly)* throw new ReadOnlyBufferException();* return offset;* }*/publicclassNIOTest11 {
publicstaticvoidmain(String[] args) {
getArrayOffsetZero();
getArrayOffset();
    }
//测试结果永远都为0的情况privatestaticvoidgetArrayOffsetZero() {
byte[] byteArray=newbyte[]{1,2,3,4,5,6};
ByteBufferbyteBuffer=ByteBuffer.wrap(byteArray);
System.out.println("byteBuffer.arrayOffset ="+byteBuffer.arrayOffset());
    }
//测试不为0的情况,偏移是相对而言的privatestaticvoidgetArrayOffset() {
byte[] byteArray=newbyte[]{1,2,3,4,5,6};
ByteBufferbyteBuffer=ByteBuffer.wrap(byteArray);
byteBuffer.position(5);
ByteBufferbyteBuffer1=byteBuffer.slice();
System.out.println("byteBuffer.arrayOffset ="+byteBuffer1.arrayOffset());
    }
}

运行结果:

byteBuffer.arrayOffset=0byteBuffer.arrayOffset=5
/*** 使用List.toArray(T[])*/publicclassNIOTest12 {
publicstaticvoidmain(String[] args) {
ByteBufferbuffer1=ByteBuffer.wrap(newbyte[]{'1','2','3','4'});
ByteBufferbuffer2=ByteBuffer.wrap(newbyte[]{'c','d','t','v'});
ByteBufferbuffer3=ByteBuffer.wrap(newbyte[]{'x','m','a','n'});
List<ByteBuffer>list=newArrayList<>();
list.add(buffer1);
list.add(buffer2);
list.add(buffer3);
ByteBuffer[] byteBufferArray=newByteBuffer[list.size()];
list.toArray(byteBufferArray);
System.out.println(byteBufferArray.length);
for (inti=0; i<byteBufferArray.length;i++){
ByteBuffereachByteBuffer=byteBufferArray[i];
while (eachByteBuffer.hasRemaining()){
System.out.print((char)eachByteBuffer.get());
            }
System.out.println();
        }
    }
}

运行结果:

31234cdtvxman
/*** 进行测试,包装wrap数据的处理*/publicclassNIOTest13 {
publicstaticvoidmain(String[] args) {
byte[] byteArray=newbyte[]{1, 2, 3, 4, 5, 56};
ByteBufferbyteBuffer=ByteBuffer.wrap(byteArray);
ByteBufferbyteBuffer1=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());
    }
}

运行结果:

byteBuffercapacity=6postion=0byteBuffer1capacity=6postion=2

了解完Buffer之后,我们就可以接着了解ByteBuffer和CharBuffer了。


目录
相关文章
|
缓存 网络协议 Java
Java NIO学习(二):Channel通道
Java NIO 的通道类似流,但又有些不同:
167 0
Java NIO学习(二):Channel通道
|
设计模式 缓存 网络协议
Java NIO学习(一):Java NIO概述
IO 的操作方式通常分为几种:同步阻塞 BIO、同步非阻塞 NIO、异步非阻塞 AIO。
158 0
Java NIO学习(一):Java NIO概述
NIO同步非阻塞模型学习使用
NIO同步非阻塞模型学习使用
NIO同步非阻塞模型学习使用
|
Java 测试技术 容器
NIO 下的 ByteBuffer简单学习
NIO 下的 ByteBuffer简单学习
117 0
|
NoSQL 应用服务中间件 Redis
NIO学习四-Selector
前面我们已经简单的学习了channel,知道channel作为通道,可以在通道中进行读写操作,同时知道ByteChannel是双向的。对于NIO的优势在于多路复用选择器上,在Nginx、Redis、Netty中都有多路复用的体现。因此学习Selector是有必要的。
88 0
NIO学习四-Selector
|
Java API
NIO学习三-Channel
在学习NIO时,ByteBuffer、Channel、Selector三个组件是必须了解的。前面我们说到ByteBuffer是作为缓冲区进行数据的存放或者获取。通常我们需要进行flip翻转操作,但是这个在Netty中,有一个更为强大的类可以替代ByteBuf,其不需要进行翻转,也可以进行读写的双向操作。要将数据打包到缓冲区中,通常需要使用通道,而通道作为传输数据的载体,也即它可以使数据从一端到另一端,因此就必须进行了解。 Channel中,我们也看到其子类有很多,通常都是用于读写操作的。其中ByteChannel可以进行读写操作,也即可以进行双向操作。 操作过程:首先创建流对象,有了流对象获取
86 0
NIO学习三-Channel
NIO学习二-ByteBuffer
前面我们已经了解到Buffer中,0<=mark<=postion<=limit<=capacity。其中mark是标记,如果为-1时丢弃。postion是当前位置,limit是限制,也即上界。capacity是容量。同时了解了直接缓冲区与缓冲区的底层实现是不同的,缓冲区是基于数组的,而直接缓冲区是基于内存的。同时可以基于反射,拿到cleaner,进而拿到clean进行清理。同时clear是还原缓冲区的状态,flip是反转缓冲区的,rewind重绕缓冲区,标记清除。remianing对剩余元素的个数记录。offset获取偏移量。 ByteBuffer是Buffer的子类,可以在缓冲区以字节为单
99 0
NIO学习二-ByteBuffer
|
Java Apache
学习socket nio 之 mina实例(1)
学习socket nio 之 mina实例(1)
132 0
学习socket nio 之 mina实例(1)
学习socket nio 之 mina实例(2)
学习socket nio 之 mina实例(2)
143 0
【Netty】netty学习之nio网络编程的模型
【一】NIO服务器编程结构   【二】Netty3.x服务端线程模型
1650 0