个人随笔
目录
一、实战-缓冲区Buffer(NIO)
2019-03-11 23:21:04

这里,不先去解释啥叫NIO,不先去说那些理论上的东西,直接先实战Buffer,后面几篇文章再详细说明一下啥是NIO。

一、缓冲区(Buffer)

在Java NIO中负责数据的存取。缓冲区就是数组,用于存储不同类型的数据
根据数据类型不同(boolean除外)了,提供了相应的缓冲区:

  1. BytrBuffer
  2. CharBuffer
  3. ShortBuffer
  4. IntBuffer
  5. LongBuffer
  6. FloatBuffer
  7. DoubleBuffer

上述缓缓冲区的管理方式几乎一致,通过allocate()获取缓冲区。

二、缓冲区存取数据的两个核心方法

  1. put():存入数据到缓冲区中
  2. get():获取缓冲区中的数据

因为很多人都会混淆流,通道,缓冲区的读写方法,这里按我理解(不一定准确的解释,但是方便记),不管是通道还是流还是缓冲区,只要是从外面到里面,都是read,get,从里面到外面,write,put,比如从硬盘到内存。从缓冲区到内存(其实都是内存,但是我觉得缓冲区在外面一层),都是read,get,而反过来就都是write,put。

三、缓冲区中的四个核心属性

属性 说明
capacity 容量,表示缓冲区中最大存储数据 的容量,一旦声明不可改变
limit 界限,表示缓冲区中可以操作数据的大小
position 位置,表示缓冲区中正在操作数据的位置
mark 标记,标记当前position的位置,可以通过reset()恢复德奥markd的位置

其中
0 <= mark <= position <=limit <=capacity

四、直接缓冲区与非直接缓冲区

名称 意义
非直接缓冲区 通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中。
直接缓冲区 通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。

因为不需要再拷贝一份到JVM内存中,所以可以提高效率,但是也有确缺点,因为这部分内存JVM是不能操作的,驻留在常规的垃圾回收堆之外。

五、Buffer的几个常用方法及说明

方法 作用
allocate() 调用方式:ByteBuffer buf= ByteBuffer.allocate(1024);分配一个指定大小的缓冲区,这里是1024字节,我们用的最多的就是ByteBuffer
allocateDirect() 创建一个直接缓冲区
put() 缓冲区初分配完大小后,开始是处于写模式,利用put存入数据到缓冲区中(相当于从内存到缓冲区,从里面到外面用put)
flip() 假如我们要读取缓冲区里面的数据,那么我们需要用flip切换为读取数据的模式
get() 利用get方法读取缓冲区中的数据(这里是从缓冲区到内存,所以相当于从外到里就用get)
rewind() 调用这个方法可以让position重新变为0,相当于可以重新读取数据
clear() 清空缓冲区,继续下一次读,其实这里只是把position的位置变为0,并没有真正清空缓冲区,数据是处于被遗忘的状态,此时limit也变成1024
mark() 标记当前position的位置,然后这里用reset()来恢复到mark的位置
reset() 恢复到mark标记的位置
hasRemaining() 判断缓冲区中是否还有剩余的数据
remaining() 获取缓冲区还有多少个可操作的数据(字节),从position算起
isDirect() 判断缓冲区是否是直接缓冲区,true为是,false为否

六、代码实例

以下代码直接拷贝到eclipse即可运行

  1. package cn.myforever.nio;
  2. import java.nio.ByteBuffer;
  3. import org.junit.Test;
  4. /**
  5. * suibibk.com
  6. */
  7. public class TestBuffer {
  8. //缓冲区的基本操作
  9. @Test
  10. public void test1() {
  11. String str = "abcde";
  12. //1、分配一个指定大小的缓冲区,这里是1024字节,,我们用的最多的就是ByteBuffer
  13. //开始的时候是写数据模式
  14. ByteBuffer buf = ByteBuffer.allocate(1024);
  15. System.out.println("---------allocate()----------");
  16. //看一下各个属性的大小,理论上来说此事capacity因该为1024字节,看ByteBuffer的构造函数可以知道,limit也为1024.因为还没有数据,所以此事和position因该是0
  17. System.out.println("capacity:"+buf.capacity());
  18. System.out.println("limit:"+buf.limit());
  19. System.out.println("position:"+buf.position());
  20. //2、利用put存入数据到缓冲区中(相当于从内存到缓冲区,从里面到外面用put)
  21. buf.put(str.getBytes());
  22. System.out.println("---------put()----------");
  23. //因为str是五个字节,说以存入后capacity应该还是1024,因为是写数据的模式,所以,limit还是1024,,position因为写了数据变成了5
  24. System.out.println("capacity:"+buf.capacity());
  25. System.out.println("limit:"+buf.limit());
  26. System.out.println("position:"+buf.position());
  27. //3、上面都是写数据,接下来读数据的话需要先切换为读取数据的模式
  28. buf.flip();
  29. System.out.println("---------flip()----------");
  30. //flip(),将缓冲区改为了读模式,position变为了0,limit还是变为了5,capacity还是1024,因为改成了读取模式
  31. System.out.println("capacity:"+buf.capacity());
  32. System.out.println("limit:"+buf.limit());
  33. System.out.println("position:"+buf.position());
  34. //4、利用get方法读取缓冲区中的数据(这里是从缓冲区到内存,所以相当于从外到里就用get)
  35. byte[] dst = new byte[buf.limit()];
  36. buf.get(dst);
  37. System.out.println("---------get()----------");
  38. //get()相当于读取数据,读取了5个,所以所以position变成了5,capacity还是1024,limit还是5
  39. System.out.println("capacity:"+buf.capacity());
  40. System.out.println("limit:"+buf.limit());
  41. System.out.println("position:"+buf.position());
  42. //5、rewind()
  43. buf.rewind();
  44. System.out.println("---------rewind()----------");
  45. //rewind()相当于让用户可以重复读数据,所以。position变回了0,capacity还是1024,limit还是5
  46. System.out.println("capacity:"+buf.capacity());
  47. System.out.println("limit:"+buf.limit());
  48. System.out.println("position:"+buf.position());
  49. //6、clear()
  50. buf.clear();
  51. System.out.println("---------clear()----------");
  52. //clear()相当于清空缓冲区,所以。position变回了0,capacity还是1024,limit变为1024
  53. System.out.println("capacity:"+buf.capacity());
  54. System.out.println("limit:"+buf.limit());
  55. System.out.println("position:"+buf.position());
  56. //6、这里就是相当于可以重新读了,用mark()来标记一下
  57. System.out.println("---------mark()----------");
  58. byte[] dst2 = new byte[buf.limit()];
  59. //先读取两个字节
  60. buf.get(dst2,0,2);
  61. System.out.println("ddst2:"+new String(dst,0,2));
  62. //此事position的位置应该是2
  63. System.out.println("postion:"+buf.position());
  64. //用mark()标记一下位置,position位置为2的位置
  65. buf.mark();
  66. //再去读俩个字节
  67. buf.get(dst2,2,2);
  68. //此时位置应该是4
  69. System.out.println("position:"+buf.position());
  70. //然后这里用reset()来恢复到mark的位置
  71. buf.reset();
  72. //此时位置应该变为2
  73. System.out.println("position:"+buf.position());
  74. //判断缓冲区中是否还有剩余的数据
  75. if(buf.hasRemaining()) {
  76. //查看还有多少个可操作的数据,因为位置变为了2,所以这里应该还有三个
  77. System.out.println(buf.remaining());
  78. }
  79. //创建一个直接缓冲区
  80. ByteBuffer buf2 = ByteBuffer.allocateDirect(1024);
  81. //用isDirect()方法来判断是直接缓冲区还是非直接缓冲区
  82. System.out.println(buf.isDirect());
  83. System.out.println(buf2.isDirect());
  84. }
  85. }

结语

上面介绍了缓冲区的一些基本规则和用法,其实可以提前说一句,在NIO中,数据的传输就是用缓冲区来,通道只是为了建立连接,形象来说,缓冲区是在通道上面跑,通道仿佛是铁路,缓冲区是火车,但是以前的流就直接是水管,直接传输信息。这里我还对流的read、write,通道的read、write,缓冲区的get、put做一个说明,很多人学到这些地方都会有点懵,到底是用read还是write。按我的记忆方法来说:只要是从外面到里面就用read,get;反之,只要是从里面到外面,就用write,put。比如

例子 说明
流(Stream)和程序 硬盘到程序(内存),也就是从外面到里面用read.从内存写到硬盘,也就是从里面到外面就用write
缓冲区和程序 从缓冲区到程序,表示从外面到里面,用get,程序写东西到缓冲区,表示从里面到外面就用put
通道和缓冲区 很明显,从通道到缓冲区,就是从外面到里面,用read,从缓冲区到通道,就是从里面到外面用write

也许我的理解方式不适合大家,但是我只是给个参考而已。:bowtie:

 193

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2