Java学习笔记--初识NIO
2017-02-17 22:51
423 查看
NIO(New IO)顾名思义,即使高级的输入/输出处理API。
相对于用InputStream和OutputStream来衔接数据源与目的地,NIO使用频道(channel)来衔接数据的节点,在处理数据的时候,NIO可以让我们设定缓冲区的容量,在缓冲区中对感兴趣的区块进行标记,像是标记读取位置,数据有效位置,对于这些区块标记,提供了clear(), rewind(), flip(), compact()等高级操作。我们使用NIO来将上面的代码改写一下:
对于上面的代码,我们在了解相关API之后就会明白。
ByteChannel中没有定义任何方法,单纯的继承了ReadableByteChannel与WritableByteChannel的行为,ByteChannel的子接口SeekableByteChannel可以读取与改变下一个要存取数据的位置。
Channel的操作类都是抽象类,不能直接实例化,想要取得Channel的操作对象,可以使用Channels类,前面定义了静态方法newChannel(),可以让你从InputStream和OutputStream分别建立ReadableByteChannel与WritableByteChannel,有些InputStream和OutputStream实例本身也有方法可以取得Channel实例,举例来说,FileInputStream,FileOutputStream都有个getChannel方法可以分别取得FileChannel实例。
如果你已经有了相关的Channel实例,也可以通过Channels上其他的new×××()静态方法,得到InputStream,OutputStream,Reader,Writer实例。
对于ByteBuffer还有一个allocateDirect方法,相对于allocate方法配置的内存是由JVM管理,它会利用操作系统的原生I/O操作,具体差异大家有兴趣可以去学习,想要知道Buffer是否为直接配置,可以通过isDirect方法得知。
Buffer是个容器,你填装的数据不会超过它的容量,实际可读取或写入的数据界限索引值可以由limit()方法得知或设定,举例来说,容量为1024字节的ByteBuffer,ReadableByteChannel对其写入了512字节,那么limit方法应该设定512,至于下一个可读取数据的位置索引值,可以使用position方法得知或设定。
现在如果要对ByteBuffer中已写入的16字节进行读取,position必须为0,limit必须为16,所以我们可以使用buffer.position(0), buffer.limit(16)来完成这项任务,但是我们直接调用flip方法,它可以将limit的值设为position的目前值,而position的值设为0.
现在我相信大家对于之前的程序已经完全明白了。
顺便给大家贴上完整的范例:
调用rewind(),会将position设置为0,但limit不变,这通常用在想要重复读取Buffer中某段数据时使用,作用相当于单独调用Buffer的position(0)方法。
NIO概述
InputStream和OutputStream的输入/输出,基本上是以字节为单位进行处理的,而且是整块数据读入后又整块数据写出,比如:public void dump(InputStream src, OutputStream dest) throws IOException{ try(InputStream input = src; OutputStream output = dest){ byte[] data = new byte[1024]; int length; while((length = input.read(data)) != -1){ output.write(data, 0, length); } } }
相对于用InputStream和OutputStream来衔接数据源与目的地,NIO使用频道(channel)来衔接数据的节点,在处理数据的时候,NIO可以让我们设定缓冲区的容量,在缓冲区中对感兴趣的区块进行标记,像是标记读取位置,数据有效位置,对于这些区块标记,提供了clear(), rewind(), flip(), compact()等高级操作。我们使用NIO来将上面的代码改写一下:
public static void dump(ReadableByteChannel src, WritableByteChannel dest) throws IOException{ ByteBuffer byteBuffer = ByteBuffer.allocate(1024); try (ReadableByteChannel readableByteChannel = src; WritableByteChannel writableByteChannel = dest){ while(readableByteChannel.read(byteBuffer) != -1){ //标记byteBuffer读入数据所在的区域 byteBuffer.flip(); writableByteChannel.write(byteBuffer); //清除byteBuffer中的标记 byteBuffer.clear(); } } }
对于上面的代码,我们在了解相关API之后就会明白。
Channel架构与操作
Channel是AutoCloseable的子接口,我们先来看一下Channel的继承架构:ByteChannel中没有定义任何方法,单纯的继承了ReadableByteChannel与WritableByteChannel的行为,ByteChannel的子接口SeekableByteChannel可以读取与改变下一个要存取数据的位置。
Channel的操作类都是抽象类,不能直接实例化,想要取得Channel的操作对象,可以使用Channels类,前面定义了静态方法newChannel(),可以让你从InputStream和OutputStream分别建立ReadableByteChannel与WritableByteChannel,有些InputStream和OutputStream实例本身也有方法可以取得Channel实例,举例来说,FileInputStream,FileOutputStream都有个getChannel方法可以分别取得FileChannel实例。
如果你已经有了相关的Channel实例,也可以通过Channels上其他的new×××()静态方法,得到InputStream,OutputStream,Reader,Writer实例。
Buffer架构与操作
在NIO设计中,数据都是在java.nio.Buffer中处理,Buffer是个抽象类,定义了clear(), flip(), reset(), rewind()等对数据区块的高级操作。容量,界限与存取位置
根据不同的数据类型处理需求,你可以选择不同的buffer子类,他们都是抽象类,不能直接实例化,然而Buffer的直接子类们都有一个allocate()静态方法,可以让我们指定Buffer的容量,如果是ByteBuffer,容量是指内部操作时使用的byte[]长度,如果是Charbuffer,则是char[]长度,FloatBuffer则是float[]长度,以此类推。Buffer容量的大小可以使用capacity方法取得,如果想取的Buffer内部的数组,可以使用array方法,如果我们有一个数组想要转为某个Buffer子类实例,每个Buffer子类实例都有wrap静态方法可以提供这项服务。对于ByteBuffer还有一个allocateDirect方法,相对于allocate方法配置的内存是由JVM管理,它会利用操作系统的原生I/O操作,具体差异大家有兴趣可以去学习,想要知道Buffer是否为直接配置,可以通过isDirect方法得知。
Buffer是个容器,你填装的数据不会超过它的容量,实际可读取或写入的数据界限索引值可以由limit()方法得知或设定,举例来说,容量为1024字节的ByteBuffer,ReadableByteChannel对其写入了512字节,那么limit方法应该设定512,至于下一个可读取数据的位置索引值,可以使用position方法得知或设定。
clear(), flip()与rewind()
Buffer的操作可以先从clear(), flip()与rewind()开始认识,当一个缓冲区刚被配置好或调用clear方法之后,limit()等于capacity(), position()会是0,如果ReadableByteChannel对ByteBuffer写入了16字节,那么position()就是16.现在如果要对ByteBuffer中已写入的16字节进行读取,position必须为0,limit必须为16,所以我们可以使用buffer.position(0), buffer.limit(16)来完成这项任务,但是我们直接调用flip方法,它可以将limit的值设为position的目前值,而position的值设为0.
现在我相信大家对于之前的程序已经完全明白了。
顺便给大家贴上完整的范例:
public class NIOUtil { public static void dump(ReadableByteChannel src, WritableByteChannel dest) throws IOException{ try (ReadableByteChannel readableByteChannel = src; WritableByteChannel writableByteChannel = dest){ ByteBuffer byteBuffer = ByteBuffer.allocate(1024); while(readableByteChannel.read(byteBuffer) != -1){ byteBuffer.flip(); writableByteChannel.write(byteBuffer); byteBuffer.clear(); } } } public static void main(String[] args) throws Exception{ URL url = new URL("https://www.baidu.com"); //得到输入源,使用Channels的静态方法创建输入流 ReadableByteChannel src = Channels.newChannel(url.openStream()); //得到输出源,使用Channels的静态方法创建输出流 WritableByteChannel dest = new FileOutputStream("index.html").getChannel(); //FileChannel dest = new FileOutputStream("index.html").getChannel(); NIOUtil.dump(src, dest); } }
调用rewind(),会将position设置为0,但limit不变,这通常用在想要重复读取Buffer中某段数据时使用,作用相当于单独调用Buffer的position(0)方法。
mark(), reset(), remaining()
Buffer上还有个mark方法,可以在目前position上标记,在存取Buffer之后,若调用了reset方法,会将那个position设回被mark标记的位置。position与limit之间为剩余可存取的资料,可以使用remaining得知还有多少长度,使用hasRemaining可以测试是否还有剩余可存取的数据。相关文章推荐
- Java NIO 学习笔记 - ByteBuffer (早期笔记)
- Java nio 学习笔记(三)
- 初识单例模式(java学习笔记)
- Java nio 学习笔记 相关知识
- 黑马程序员(学习笔记二)初识java特点
- Java nio 学习笔记(六)
- Java NIO 学习笔记 - SocketChannel
- Java NIO 学习笔记
- Java nio 学习笔记(三)
- 学习笔记:java并发编程学习之初识Concurrent
- Java nio 学习笔记(一) Buffer(缓冲区)与Channel(通道)的相关知识
- Java nio 学习笔记(三)
- Java nio 学习笔记(六)
- Java nio 学习笔记(四) 淘宝2012校招技术笔试题
- java NIO非阻塞式IO网络编程学习笔记(一)
- Java nio 学习笔记(二) Charset(字符集)与Selector(异步IO)的知识
- 转载:Java NIO 学习笔记 - ByteBuffer
- Java nio 学习笔记(一) Buffer(缓冲区)与Channel(通道)的相关知识
- 初识Java——(Java学习笔记一)
- Java NIO 学习笔记 selector 行为机制分析(select操作 cancel操作)