前面说的输入输出流都是阻塞式的.而且传统的输入输出流都是通过字节的移动来处理(即使不直接处理字节,底层还是依赖字节处理),也就是说面向输输出系统一次只能处理一个字节,所以效率并不高.
新IO概述
新IO使用不同的方式来处理输入输出.采用内存映射文件的方式来处理输入输出.它将文件的一段区域映射到内存中,像访问内存一样访文件(模拟了操作系统上虚拟内存的概念). NIO中主要包包括:
-
java.nio
和Buffer相关的类 -
java.nio.channels
包括Channel和Selector相关类 -
java.nio.charset
和字符集相关的类 -
java.nio.charset.spi
提供字符集服务的相关类
Channel
(通道)和Buffer
(缓冲)是新IO中的两个核心对象,Channel是对传统输入输出系统的模拟.新IO系统中所有数据都要通过通道传输;Channel
与传统的InputStream,OutputStream最大区别在于提供了一个map
方法,通过该map
方法可以直接将"一块数据"映射到内存中.如果说传统的输入输出是面向流的处理,而新IO则是面向块的处理.
Buffer
可以被理解成一个容器,本质是一个数组,发送到Channel中所有对象都必须首先放到Buffer
中,从而Channel
的读数据也必须先读到Buffer
中.Buffer
允许一次次的取数据,也允许使用Channel
直接将文件的某块映射成Buffer
.
Buffer
结构上看,Buffer
像一个数组,保存多个类型的相同数据.Buffer
是一个抽象类.最常用子类是ByteBuffer
,可在底层字节数组上做get/set操作.除ByteBuffer
之外,对应其他基本数据类型(boolean除外)都有相对应的Buffer
,ByteBuffer
,CharBuffer
,CharBuffer
,ShortBuffer
,IntBuffer
等.这些类除了ByteBuffer
之外,都采用类似或相同的方法来管理数据.只是各自管理的对象不同而已.这些Buffer
都没有提供构造器,通过如下方法得到一个Buffer
对象:
static XxxBuffer allocate(int capacity)
创建一个容量为capacity的XxxBuffer对象
实际中使用较多的是ByteBuffer
和CharBuffer
.其他Buffer
子类则较少使用.其中ByteBuffer
的子类MappedByteBuffer
,它用于表示Channel
将磁盘文件的全部或部分映射到内存中得到的结果,通常MapByteBuffer
对象由Channel
的map
对象返回.
Buffer中三个重要概念
- 容量(capacity) 缓冲区容量,表示该Buffer的最大数据容量.即最多可以存储多少数据.容量不能为负值,创建后也不可改变.
- 界限(limit) 第一个不应该被读出或者写入的缓冲区位置索引.limit后的位置既不可被读取,也不可被写.
- 位置(position) 用于指明下一个可以被读出的或写入缓冲区位置索引(类似IO流中的记录指针).Buffer从Channel读取数据时,position的位置等于恰好已经读了多少数据,创建Buffer对象时,position为0,从Channel读取了2个数据,position为2,指向Buffer中的第3的位置(第一个索引为0).
此外Buffer还支持一个可选标记mark,该mark允许直接将position指定位到mark处.这些值满足如下关系:0 <= mark <= position <= limit <= capacity
Buffer的主要作用就是装载数据,然后输出数据.开始时Buffer的position为0,limit为capacity.程序不断调用put向Buffer中放入数据(或从channel获取数据),每放入一些数据,position向后移动一些位置.
当Buffer装入数据结束后,调用filp方法,该方法将limit设置为position所在位置,将position设置为0.这样使得从Buffer中读取数据总是从0开始.读完所有装入的数据即结束.也就是说.Buffer调用filp后,Buffer为输出数据做好了准备.
当Buffer输出数据结束后,调用clear方法.将position置为0,将limit置为capacity,这样为再次向Buffer中装载数据做好准备.
Buffer中的常用方法:
* int capacity()
返回Buffer的capacity大小
* boolean hasRemaining()
判断当前位置(position)和界限(limit)之间是否还有元素可供处理.
* int limit()
返回Buffer的界限(limit)的位置
* Buffer limit(int newLt)
重新设置界限(limit)的值,并返回一个具有新的limit的缓冲区对象.
* Buffer mark()
设置Buffer的mark的位置,只能在0和position之间.
* int position
返回当前Buffer中的当前位置.
* Buffer position(int newPs)
设置Buffer的新位置,并返回一个具有改变position后的Buffer对象.
* int remaining()
返回当前位置和界限(limit)之间的元素个数
* Buffer reset()
将位置(position)转到mark所在的位置
* Buffer rewind()
将位置(position)设置为0,取消mark.
之外Buffer的所有子类还支持put/get方法.对Buffer进行数据的放入和取出.使用put/get来访问Buffer中数据时,分为绝对和相对两种: * 相对(Relative) 从Buffer当前位置读取或写入数据,然后将位置(position)的值按处理元素个数增加. * 绝对(Ansolut) 直接根据索引来向Buffer中读取或写入数据,使用绝对方式来访问Buffer里的数据,并不会影响position的值.
//省略代码
CharBuffer cbuf = CharBuffer.allocate(8);
System.out.println("capacity:"+ cbuf.capacity());
System.out.println("limit" + cbuf.limit());
System.out.println("position:"+ cbuf.position());
cbuf.put('a');
cbuf.put('b');
cbuf.put('c');
System.out.println("加入三个元素后,position:" + cbuf.position());
cbuf.flip();
System.out.println("执行filp,limit:" + cbuf.limit());
System.out.println("position:" + cbuf.position());
//取出第一个元素
System.out.println("取出第一个元素:" + cbuf.get());
System.out.println("取出第一个元素后,position:" + cbuf.position());
cbuf.clear();
//limit置为capacity
System.out.println("执行clear后,limit:" + cbuf.limit());
//capacity置0
System.out.println("执行clear后,position:" + cbuf.position());
//clear方法不清除缓冲区(buffer)中数据
System.out.println("执行clear后,缓冲区内容没有被清除:" + cbuf.get(2));
//绝对读取不影响position位置
System.out.println("执行绝对读取后,position:" + cbuf.position());
//省略代码
Channel
//待更新