????????从JDK1.4开始,java提供了一系列改进的IO处理(NIO:New IO),NIO可以替代标准的Java IO API,并且与标准的IO工作方式不同。
Java NIO 是一种同步非阻塞式IO。
Java中与新IO相关的包如下: ????????? java.nio包:主要包含各种与Buffer相关的类。 ????????? java.nio.channels包:主要包含与Channel和Selector相关的类。 ????????? java.nio.charset包:主要包含与字符集相关的类。 ????????? java.nio.channels.spi包:主要包含与Channel相关的服务提供者编程接口。 ????????? java.nio.charset.spi包:包含与字符集相关的服务提供者编程接口。
New IO中的三个核心对象:Channel(通道)和Buffer(缓冲);
- Channel是对传统的输入/输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输;
- Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先放到Buffer中。
New IO提供了支持非阻塞式输入/输出的Selector(选择器)类;
????????选择器是一个可以监视多个事件通道的对象(例如:连接打开、数据到达等)。因此,单个线程可以监视多个通道的数据。
其余的组件,如?Pipe 和FileLock 只是与三个核心组件结合使用的实用程序类。
Buffer
Buffer是一个抽象类。
Buffer就像一个数组,它可以保存多个类型相同的数据,可以在底层字节数组上进行get/set操作。
Buffer的类型: ????????CharBuffer ????????byteBuffer(最常用,但也与其他Buffer有点不同) ????????ShortBuffer ????????IntBuffer ????????LongBuffer ????????FloatBuffer ????????DoubleBuffer (boolean类型没有对应的Buffer)
这些Buffer类都没有提供构造器,通过使用如下方法来得到一个Buffer对象。
static XxxBuffer allocate(int capacity):创建一个容量为capacity的XxxBuffer对象。(不同Buffer都有的类方法)
Buffer中有三个重要的属性:容量(capacity)、界限(limit)和位置(position)。
容量:Buffer相当于一个内存块,而容量就是这个内存块所能存储的该类型元素的数量,言外之意就是这个内存块的大小,容量不为负数且创建之后大小不能改变。
界限:第一个不应该被读出或者写入的缓冲区的元素的位置索引。也就是说,位于limit后的数据既不可被读,也不可被写。限制永不为负数也永远不会大于容量。
位置:用于指明下一个可以被读出的或者写入的缓冲区位置索引(类似于IO流中的记录指针)。说明在position之前的缓冲区已经被使用,从position开始到limit之间的缓冲区未被使用。位置也永不为负数也永远不大于其界限。
0 <= mark <= position <= limit <= capacity
0-capacity:Buffer的空间大小
0-limit:表示Buffer中可以使用的空间大小
0-position:表示Buffer中已使用的空间大小
position-limit:表示buffer中可以使用但尚未使用的空间大小
(这里的大小指的是该Buffer对应的类型元素的数量;Buffer里还支持一个可选的标记(mark,类似于传统IO流中的mark),Buffer允许直接将position定位到该mark处。)
Buffer的主要作用就是装入数据,然后输出数据。
创建Buffer:? ? ? ?
//创建大小为48个byte元素大小的ByteBuffer,capacity为48
ByteBuffer buf = ByteBuffer.allocate(48);
//从通道中映射一个大小为1024个char元素大小的CharBuffer,capacity为1024
CharBuffer buf = CharBuffer.allocate(1024);
使用Buffer通常遵循4个步骤:
1、数据装入Buffer
????????开始时:Buffer的position为0,limit为capacity,程序可通过put()方法向Buffer中放入一些数据(或者从Channel中获取一些数据),每放入一些数据,Buffer的position相应地向后移动一些位置。
2、调用Buffer的flip()方法
????????该方法将limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移到了开始位置。也就是说,Buffer调用flip()方法之后,Buffer为输出数据做好准备。
3、读取数据
????????使用get()系列的方法读取数据。
4、调用Buffer的clear()方法或者compact()方法,为再次向Buffer中装入数据做好准备
? ? ? ??clear() 方法清除(注意:这里的清除并不是字面意思)整个缓冲区,clear()方法不是清空Buffer的数据,它仅仅将position置为0,将limit置为capacity,这样为再次向Buffer中装入数据做好准备。
??compact() ?方法仅清除(注意:这里的清除并不是字面意思)已读取的数据。任何未读数据都被移到缓冲区的开头,数据将在未读数据之后写入缓冲区。
?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)之间做mark。 ? int position():返回Buffer中的position值。 ? Buffer position(int newPs):设置Buffer的position,并返回position被修改后的Buffer对象。 ? int remaining():返回当前位置和界限(limit)之间的元素个数。 ? Buffer reset():将位置(position)转到mark所在的位置。 ? Buffer rewind():将位置(position)设置成0,取消设置的mark。
????????Buffer的所有子类还提供了两个重要的方法:put()和get()方法,用于向Buffer中放入数据和从Buffer中取出数据。当使用put()和get()方法放入、取出数据时,Buffer既支持对单个数据的访问,也支持对批量数据的访问(以数组作为参数)。?
注意:
????????使用put()和get()来访问Buffer中的数据时,分为相对和绝对两种。 ? 相对(Relative):从Buffer的当前position处开始读取或写入数据,然后将位置(position)的值按处理元素的个数增加。(直接get())
? 绝对(Absolute):直接根据索引向Buffer中读取或写入数据,使用绝对方式访问Buffer里的数据时,并不会影响位置(position)的值。(get(int index),需要给出索引值index)
equals() 和 compareTo()?:
????????equals() 和compareTo()方法是用于比较两个缓冲区。 ?
equals()?如果满足以下条件,则两个缓冲区相等:
- 它们的类型相同(字节、字符、整数等)
- 它们在缓冲区中具有相同数量的剩余字节、字符等(position要在相同的位置)。
- 所有剩余的字节、字符等都是相等的(position位置之后上的每一个元素也要相同)。
compareTo()??方法比较两个缓冲区的剩余元素(字节、字符等)
????注意:在以下情况下,缓冲区被认为比另一个缓冲区“小”:
- 依次比较剩余元素的大小,直至元素不相等,直接返回比较结果。
- 如果所有元素都是相等的,但第一个缓冲区在第二个缓冲区之前用完元素(它有更少的元素),那么第一个缓冲区小于第二个缓冲区。
|