原文 http://blog.csdn.net/u012345283/article/details/38357851java
缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据做临时存储,这部分预留的内存空间就叫作缓冲区:数组
使用缓冲区有这么两个好处:app
一、减小实际的物理读写次数工具
二、缓冲区在建立时就被分配内存,这块内存区域一直被重用,能够减小动态分配和回收内存的次数post
举个简单的例子,好比A地有1w块砖要搬到B地性能
因为没有工具(缓冲区),咱们一次只能搬一本,那么就要搬1w次(实际读写次数)测试
若是A,B两地距离很远的话(IO性能消耗),那么性能消耗将会很大大数据
可是要是此时咱们有辆大卡车(缓冲区),一次可运5000本,那么2次就够了ui
相比以前,性能确定是大大提升了。spa
因此,buffer在IO中很重要。在旧I/O类库中(相对java.nio包)中的BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter在其实现中都运用了缓冲区。java.nio包公开了Buffer API,使得Java程序能够直接控制和运用缓冲区。
在Java NIO中,缓冲区的做用也是用来临时存储数据,能够理解为是I/O操做中数据的中转站。缓冲区直接为通道(Channel)服务,写入数据到通道或从通道读取数据,这样的操利用缓冲区数据来传递就能够达到对数据高效处理的目的。在NIO中主要有八种缓冲区类(其中MappedByteBuffer是专门用于内存映射的一种ByteBuffer):
全部缓冲区都有4个属性:capacity、limit、position、mark,并遵循:mark <= position <= limit <= capacity,下表格是对着4个属性的解释:
属性 | 描述 |
---|---|
Capacity | 容量,便可以容纳的最大数据量;在缓冲区建立时被设定而且不能改变 |
Limit | 表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操做。且极限是能够修改的 |
Position | 位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变改值,为下次读写做准备 |
Mark | 标记,调用mark()来设置mark=position,再调用reset()可让position恢复到标记的位置 |
一、实例化
java.nio.Buffer类是一个抽象类,不能被实例化。Buffer类的直接子类,如ByteBuffer等也是抽象类,因此也不能被实例化。
可是ByteBuffer类提供了4个静态工厂方法来得到ByteBuffer的实例:
方法 | 描述 |
---|---|
allocate(int capacity) | 从堆空间中分配一个容量大小为capacity的byte数组做为缓冲区的byte数据存储器 |
allocateDirect(int capacity) | 是不使用JVM堆栈而是经过操做系统来建立内存块用做缓冲区,它与当前操做系统可以更好的耦合,所以能进一步提升I/O操做速度。可是分配直接缓冲区的系统开销很大,所以只有在缓冲区较大并长期存在,或者须要常常重用时,才使用这种缓冲区 |
wrap(byte[] array) | 这个缓冲区的数据会存放在byte数组中,bytes数组或buff缓冲区任何一方中数据的改动都会影响另外一方。其实ByteBuffer底层原本就有一个bytes数组负责来保存buffer缓冲区中的数据,经过allocate方法系统会帮你构造一个byte数组 |
wrap(byte[] array, int offset, int length) |
在上一个方法的基础上能够指定偏移量和长度,这个offset也就是包装后byteBuffer的position,而length呢就是limit-position的大小,从而咱们能够获得limit的位置为length+position(offset) |
我写了这几个方法的测试方法,你们能够运行起来更容易理解
public static void main(String args[]) throws FileNotFoundException { System.out.println("----------Test allocate--------"); System.out.println("before alocate:" + Runtime.getRuntime().freeMemory()); // 若是分配的内存太小,调用Runtime.getRuntime().freeMemory()大小不会变化? // 要超过多少内存大小JVM才能感受到? ByteBuffer buffer = ByteBuffer.allocate(102400); System.out.println("buffer = " + buffer); System.out.println("after alocate:" + Runtime.getRuntime().freeMemory()); // 这部分直接用的系统内存,因此对JVM的内存没有影响 ByteBuffer directBuffer = ByteBuffer.allocateDirect(102400); System.out.println("directBuffer = " + directBuffer); System.out.println("after direct alocate:" + Runtime.getRuntime().freeMemory()); System.out.println("----------Test wrap--------"); byte[] bytes = new byte[32]; buffer = ByteBuffer.wrap(bytes); System.out.println(buffer); buffer = ByteBuffer.wrap(bytes, 10, 10); System.out.println(buffer); }
二、另一些方法
方法 | 描述 |
---|---|
limit(), limit(10)等 | 其中读取和设置这4个属性的方法的命名和jQuery中的val(),val(10)相似,一个负责get,一个负责set |
reset() | 把position设置成mark的值,至关于以前作过一个标记,如今要退回到以前标记的地方 |
clear() | position = 0;limit = capacity;mark = -1; 有点初始化的味道,可是并不影响底层byte数组的内容 |
flip() | limit = position;position = 0;mark = -1; 翻转,也就是让flip以后的position到limit这块区域变成以前的0到position这块,翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态 |
rewind() | 把position设为0,mark设为-1,不改变limit的值 |
remaining() | return limit - position; 返回limit和position之间相对位置差 |
hasRemaining() | return position < limit 返回是否还有未读内容 |
compact() | 把从position到limit中的内容移到0到limit-position的区域内,position和limit的取值也分别变成limit-position、capacity。若是先将positon设置到limit,再compact,那么至关于clear() |
get() | 相对读,从position位置读取一个byte,并将position+1,为下次读写做准备 |
get(int index) | 绝对读,读取byteBuffer底层的bytes中下标为index的byte,不改变position |
get(byte[] dst, int offset, int length) | 从position位置开始相对读,读length个byte,并写入dst下标从offset到offset+length的区域 |
put(byte b) | 相对写,向position的位置写入一个byte,并将postion+1,为下次读写做准备 |
put(int index, byte b) | 绝对写,向 byteBuffer底层的bytes中下标为index的位置插入byte b,不改变position |
put(ByteBuffer src) | 用相对写,把src中可读的部分(也就是position到limit)写入此byteBuffer |
put(byte[] src, int offset, int length) | 从src数组中的offset到offset+length区域读取数据并使用相对写写入此byteBuffer |
如下为一些测试方法:
public static void main(String args[]) throws FileNotFoundException { System.out.println("--------Test reset----------"); buffer.clear(); buffer.position(5); buffer.mark(); buffer.position(10); System.out.println("before reset:" + buffer); buffer.reset(); System.out.println("after reset:" + buffer); System.out.println("--------Test rewind--------"); buffer.clear(); buffer.position(10); buffer.limit(15); System.out.println("before rewind:" + buffer); buffer.rewind(); System.out.println("before rewind:" + buffer); System.out.println("--------Test compact--------"); buffer.clear(); buffer.put("abcd".getBytes()); System.out.println("before compact:" + buffer); System.out.println(new String(buffer.array())); buffer.flip(); System.out.println("after flip:" + buffer); System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println("after three gets:" + buffer); System.out.println("\t" + new String(buffer.array())); buffer.compact(); System.out.println("after compact:" + buffer); System.out.println("\t" + new String(buffer.array())); System.out.println("------Test get-------------"); buffer = ByteBuffer.allocate(32); buffer.put((byte) 'a').put((byte) 'b').put((byte) 'c').put((byte) 'd') .put((byte) 'e').put((byte) 'f'); System.out.println("before flip()" + buffer); // 转换为读取模式 buffer.flip(); System.out.println("before get():" + buffer); System.out.println((char) buffer.get()); System.out.println("after get():" + buffer); // get(index)不影响position的值 System.out.println((char) buffer.get(2)); System.out.println("after get(index):" + buffer); byte[] dst = new byte[10]; buffer.get(dst, 0, 2); System.out.println("after get(dst, 0, 2):" + buffer); System.out.println("\t dst:" + new String(dst)); System.out.println("buffer now is:" + buffer); System.out.println("\t" + new String(buffer.array())); System.out.println("--------Test put-------"); ByteBuffer bb = ByteBuffer.allocate(32); System.out.println("before put(byte):" + bb); System.out.println("after put(byte):" + bb.put((byte) 'z')); System.out.println("\t" + bb.put(2, (byte) 'c')); // put(2,(byte) 'c')不改变position的位置 System.out.println("after put(2,(byte) 'c'):" + bb); System.out.println("\t" + new String(bb.array())); // 这里的buffer是 abcdef[pos=3 lim=6 cap=32] bb.put(buffer); System.out.println("after put(buffer):" + bb); System.out.println("\t" + new String(bb.array())); }