Channel(通道)
Java NIO的通道类似流,但又有些不同:
既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。 通道可以异步地读写。
通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

Channel的实现
这些是Java NIO中最重要的通道的实现:
- FileChannel: 从文件中读写数据。
- DatagramChannel : 能通过UDP读写网络中的数据。
- SocketChannel: 能通过TCP读写网络中的数据。
- ServerSocketChannel :可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
Buffer(缓冲区)
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
Buffer(缓冲区)的主要属性
属性功能capacity容量position缓冲区当前位置指针,最大可为capacity – 1limit缓冲区最大读取和写入限制
buffer属性示意图:

上图展示了写模式和读模式下,以上属性的示意图,写模式下,limit和capacity是一样的,这表示你能写入的最大容量数据,读模式下,limit会和position一样,表示你能读到写入的全部数据。
Buffer(缓冲区)的主要分类
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
其实以上基本上为了接收不同的数据类型而对应的,只有一个特殊的MappedByteBuffer,本次先不学习它,后续再去了解它。
五、实战
我们主要以理解上面介绍的概念为目的实现一个简单的NIO编程,读取文件夹内的文件,然后输出到控制台。
- public
- static
- void
- testNio
- (){
- try
- {
- RandomAccessFile
- rdf
- =
- new
- RandomAccessFile
- (
- "E:\\nio\\niotest.txt"
- ,
- "rw"
- );
- //利用channel中的FileChannel来实现文件的读取
- FileChannel
- inChannel
- =
- rdf
- .
- getChannel
- ();
- //设置缓冲区容量为10
- ByteBuffer
- buf
- =
- ByteBuffer
- .
- allocate
- (
- 10
- );
- //从通道中读取数据到缓冲区,返回读取的字节数量
- int
- byteRead
- =
- inChannel
- .
- read
- (
- buf
- );
- //数量为-1表示读取完毕。
- while
- (
- byteRead
- !=-
- 1
- ){
- //切换模式为读模式,其实就是把postion位置设置为0,可以从0开始读取
- buf
- .
- flip
- ();
- //如果缓冲区还有数据
- while
- (
- buf
- .
- hasRemaining
- ()){
- //输出一个字符
- System
- .
- out
- .
- ((
- char
- )
- buf
- .
- get
- ());
- }
- //数据读完后清空缓冲区
- buf
- .
- clear
- ();
- //继续把通道内剩余数据写入缓冲区
- byteRead
- =
- inChannel
- .
- read
- (
- buf
- );
- }
- //关闭通道
- rdf
- .
- close
- ();
- }
- catch
- (
- FileNotFoundException
- e
- )
- {
- e
- .
- printStackTrace
- ();
- }
- catch
- (
- IOException
- e
- )
- {
- e
- .
- printStackTrace
- ();
- }
- }
在磁盘中niotest.txt的内容是hello world,我们看看运行结果是不是hello world。

结果和预期一样,我们完成了NIO编程输出磁盘文件内容,其实上面代码中,我第一印象有疑问的地方就是 buf.flip(),其他只要用过IO的应该都能理解,既然不能理解我们就先调试下,注释掉该段内容,看看输出结果如何。

注释掉 buf.flip() 后,输出结果果真不对了,那究竟是为啥呢?

上图是第一个循环,我们可以看到缓冲区的position=10,limit=10,cap=10。调用 buf.hasRemaining()
为false,所以buffer第一次没有输出任何东西。看看第二次循环

其实这整个循环可以解析步骤如下:
- buffer读取了10字节内容,内容就是:hello worl。
- buf.hasRemaining() 判断为false,直接清空buffer(直接把position复位为0,可以直接覆盖内容),读取剩下内容。
- 最后只剩下一个字符 d 读取,这个时候读取完后,buffer内容如下:dello worl。
- 最后输出因为从postion=1位置输出,所以输出:ello worl。
根据以上解析,我们发现注释了buf.flip()后,position位置写入是什么位置,读出就是什么位置,所以,该方法应该就是把position赋值为0,从开始读取。解读源码也证实了我的猜想,源码如下:
- public
- final
- Buffer
- flip
- ()
- {
- limit
- =
- position
- ;
- position
- =
- 0
- ;
- mark
- =
- -
- 1
- ;
- return
- this
- ;
- }
推荐阅读
- 【JVM】类加载、连接和初始化过程
- 2018全套学习视频资源已经整理好了!免费分享!
- 【资源分享】Docker核心技术视频教程
- 【资源分享】蚂蚁课堂2期无加密-Java视频教程
- Java日志体系详细总结
- 【资源分享】Spring Cloud微服务实战视频课程
- 并发编程之ThreadLocal关键字
- 详解BlockingQueue
- Springmvc源码解析总结
- myBatis 体系结构源码解读
微信:Yuanping1510
本公众号会不定期给大家发福利,包括学习资源等,敬请期待吧!
推送内容如果现在工作用不上,可以先转发朋友圈或收藏,用的时候方便找。
另外欢迎关注公众号或添加微信好友,互相学习交流。