Buffer 是一个对象, 它包含一些要写入或者刚读出的数据,即数据的缓冲区。在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别,在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。buffer + channel 类似原io包中的BufferedInputStream/BufferedOutputStream,对数据流做了缓冲,减少磁盘或者网络io次数,提高性能等。缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。
下面是Buffer的类结构图:
首先从最顶层的Buffer开始
四个私有属性用于处理缓冲区的数据。buffer类中定义如下:
private int mark = -1; private int position = 0; private int limit; private int capacity;
Capacity(容量):
缓冲区能装载的最大数据量,缓冲区被创建时被设置,并不会被更改。
Position(位置):
被读取或被写入的下一个元素的索引,调用get( ) 和 put( )方法时会关联的更新position,换句话说,写入buffer时,Position代表buffer第一个未被写入元素的索引【即put下个元素存放的位置】,读取时,Position代表buffer代表第一个未被读取的索引【即get取时第一个元素位置】。
Limit(限制):
缓冲区中不能被读取或被写入的第一元素。换句话说,在缓冲区中可以使用的元素个数,写入buffer时,Limit为Capacity即能写入buffer最多元素个数,读取buffer是Limit为buffer中元素的个数即写入状态是Position的值。
Mark(标记):
被记录的位置,调用mark( )方法时设置mark=position。调用reset( )设置position = mark,mark属性未被设置时,默认为-1;
四个属性的关系如下:
mark <= position <= limit <= capacity
创建buffer
使用的是实现类中的allocate方法。创建一个容量大小为7的ByteBuffer示例如下:
ByteBuffer readBuffer = ByteBuffer.allocate(7);
创建完毕buffer后可以被写入【即写模式】,position代表未被写入第一元素为0,被写入时limit 等于capacity为7代表缓冲区能写入的byte数,mark为-1(后面有原代码说明)。capacity为固定值7,其他三个属性在buffer被使用的能被修改。
mark属性的看下原代码,在buffer的allocate方法如下
public static ByteBuffer allocate(int capacity) { (capacity < 0) throw new IllegalArgumentException(); urn new HeapByteBuffer(capacity, capacity); }HeapByteBuffer的构造方法如下
class HeapByteBuffer extends ByteBuffer { // For speed these fields are actually declared in X-Buffer; // these declarations are here as documentation /* protected final byte[] hb; protected final int offset; */ HeapByteBuffer(int cap, int lim) { // package-private super(-1, 0, lim, cap, new byte[cap], 0); /* hb = new byte[cap]; offset = 0; */ }super为 ByteBuffer,super的各个值属性如下,
ByteBuffer(int mark, int pos, int lim, int cap, // package-private byte[] hb, int offset) { super(mark, pos, lim, cap); this.hb = hb; this.offset = offset; }从这里我们可以发现mark的被设置为-1。
上面的源码我们发现buffer实际是数组的包装,实际数据存储对象为一个数组,byteBuffer的构造函数中 new byte[cap],创建的就是一个byte的数组。
Accessing(访问,读写buffer)
API如下:
public abstract class ByteBuffer extends Buffer implements Comparable { // This is a partial API listing public abstract byte get( ); public abstract byte get (int index); public abstract ByteBuffer put (byte b); public abstract ByteBuffer put (int index, byte b); }填充:
ByteBuffer在子类HeapByteBuffer有put方法的实现如下
public ByteBuffer put(byte x) { hb[ix(nextPutIndex())] = x; return this; } //获取写入索引位置 final int nextPutIndex() { // package-private //写入范围校验,必须为position和limit之间 if (position >= limit) throw new BufferOverflowException(); //先将position返回,position再自加1 return position++; }从代码中我们可以看出put就是数据放置到数组对应的position位置,然后position=position+1指向下个空闲位置。从这里我们也看可以看出put值放入的范围为position位置到limit之间。
示例:
buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l') .put((byte)'o');
各属性值变化如下
往里进行put时,只有position值发生变化,例如上面的填充了5个字节的大小,position值为5,指上buffer里面的第6个元素。
put (int index, byte b)可以让我们操作buffer中已经操作过位置的值。
buffer.put((byte)'w');
变化后的图示如下
使用 put (int index, byte b)并不会影响到buffer的相关属性值的变化
Flipping(将读状态转换到写状态)
buffer被填充后,我们怎样将数据读出来呢,flip(),能将buffer由写模式切换至读模式。
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
其实flip主要是调整position位置为0,limit设置为position ,mark 设置为-1。
从这里我们可以看出如果执行了flip方法,直接往buffer里面写入值的话,整个buffer从0开始被重新,但是最多只能写入到limit,而如果执行读的话,我们从索引为0的开始读,最多读取limit个数据。
上个buffer执行flip后各属性状态变成为
从图可以看出buffer的position位置变为0,代表可以从0索引开始读取数据,limit设置为原来的position位置6,代表我能从索引位置0读取到5。
Draining(从buffer读取)
get方法是从buffer里面读取数据,在子类HeapByteBuffer有put方法的实现如下
public byte get() { return hb[ix(nextGetIndex())]; } //注意这里offset值为0 protected int ix(int i) { return i + offset; } //获取position值 final int nextGetIndex() { // package-private if (position >= limit) throw new BufferUnderflowException(); return position++; }
可以看出get方法获取的是position与limit之间的数据,每获取后position位置+1,即下个get索引所在位置,所以无论我们调用什么方法操作buffer后,最后使用get方法获取的都是position与limit之间的数据。
调用示例如下
buffer.flip(); System.out.println( (char)buffer.get() ); buffer.mark(); System.out.println( (char)buffer.get() ); buffer.reset(); System.out.println( (char)buffer.get() );
上面的buffer我们flip后调用get整个存储变成
position指向下个索引位置即为1,其他属性不变化
mark后存储变成:
get后:
reset后:
clear(清空buffer)
public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; }
清空比较明确,即调整position调整为0,limit 调整为capacity,mark调整为-1。我们的上面的示例buffer执行clear后各个属性情况如下
从这里我们可以看出,clear()并未实际的清空数据,而只是调整相关属性。
构造方法:
ByteBuffer没有public的构造方法,而是通过一下三种方式来创建:
public static ByteBuffer allocate(int capacity) public static ByteBuffer allocateDirect(int capacity) public static ByteBuffer wrap(byte[] array, int offset, int length)
allocate和allocateDirect分别调用了其子类HeapByteBuffer与DirectByteBuffer。前者可以看出分配的buffer是在heap区域的,而后者是通过unsafe.allocateMemory(cap + ps);在Java虚拟机外的内存中分配了一块,通过unsafe直接分配操作系统内存来使用。这块directMemory可以通过-XX:MaxDirectMemorySize来配置默认大小,通过fullGC回收。
直接使用系统内存的好处是可以减少操作系统内存到虚拟机内存的拷贝,而提高性能。
相关推荐
Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六) Selector Java NIO系列教程(七) FileChannel Java NIO系列教程(八) ...
java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...
JAVA NIO学习资料JAVA NIO学习资料
12-Java NIO-Buffer-概述.mp4 13-Java NIO-Buffer-基本使用.mp4 14-Java NIO-Buffer-三个属性和类型.mp4 17-Java NIO-Buffer-缓冲区分片.mp4 18-Java NIO-Buffer-只读缓冲区.mp4 19-Java NIO-Buffer-直接缓冲区.mp4 ...
java nio入门学习,两个pdfjava nio入门学习,两个pdf
java NIO的基本知识点学习笔记,不包含具体代码
java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...
Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据...
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...
Java NIO英文高清原版
主要介绍了Java NIO Buffer过程详解,缓冲区在java nio中负责数据的存储。缓冲区就是数组。用于存储不同数据类型的数据。,需要的朋友可以参考下
Java NIO 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...
讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用
java nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socket
javaNIO学习笔记(csdn)————程序
java学习笔记1(java io/nio)设计模式
Contents: 1 核心概念以及基本读写 2 缓冲区的实现机制 3 连网与异步IO 4 分散和聚集IO 5 文件锁定
01-Java NIO-课程简介.mp4 02-Java NIO-概述.mp4 03-Java NIO-Channel-概述.mp4 04-Java NIO-Channel-FileChannel(介绍和示例).mp4 05-Java NIO-Channel-FileChannel详解(一)....15-Java NIO-Buffer-分配和读写数据.mp4
java_nio学习文档
java侧起server(NioUdpServer1.java),基于Java Nio的selector 阻塞等候,一个android app(NioUdpClient1文件夹)和一个java程序(UI.java)作为两个client分别向该server发数据,server收到后分别打印收到的消息...