在JDK4中引入了NIO,可以最大限度的满足Java程序I/O的需求
在NIO中有三大核心组件:Channel、Buffer、Selector
传统IO与JavaNIO的区别:
IO | NIO |
---|---|
传统I/O是面向流的,每次可以从流中读取一个或多个字节,只能向后读取,不能向前移动; | NIO是面向缓冲区的,把数据读取到一个缓冲区中,可以在缓冲区中向前/向后移动,增加了程序的灵活性 |
IO流是线程阻塞的,在调用read()/write()读写数据时,线程阻塞,直到数据读取完毕或者数据完全写入,在读取过程中,线程不能做其它操作; | NIO不是线程阻塞的,当线程从Channel中读取数据时,如果通道中没有可用的数据,线程不阻塞,可以做其它的任务 |
/ | 在NIO中,所有数据都需要通过Channel传输,通道可以直接将一块数据映射到内存中,channel是双向的,不仅可以读取数据,还能保存数据,程序不能直接读写channel通道,Channel只与Buffer缓冲区交互,数据通过channel写到buffer,程序从buffer中读数据 |
1、Buffer的属性
属性 | 作用 |
---|---|
capacity | 是指缓冲区可以存储多少个数据,容量在创建buffer缓冲区时指定大小,创建后不能再修改,如果缓冲区满了,需要清空后才能继续写数据 |
position | 表示当前位置,即缓冲区写入/读取的位置,刚刚创建Buffer对象后,position初始化为0,写入一个数据,position就向后移动一个单元,它的最大值为capacity - 1,当Buffer从写模式切换到读模式,position会被重置为0,每读一个数据,position就向后移动一个单元 |
limit | 是指第一个不能被读取或写入的位置,limit上限后面的单元既不能读也不能写,在Buffer缓冲区写模式下,limit表示能够写入多少个数据;在读取模式下,limit表示最多可以读取多少个数据 |
mark | 设置一个标记位置,可以调用mark()方法,把标记设置在position位置,当调用reset()方法时,就把position设置为mark标记的位置 |
2、Buffer的API
API | 作用 |
---|---|
allocate(capacity) | 可以创建一个指定容量的缓冲区 |
put() | 用于向缓冲区中存储数据 |
get() | 用于从缓冲区中读取数据 |
compact() | 当缓冲区还有未读完的数据,可以调用compact()方法进行压缩,将所有未读取的数据复制到Buffer的起始位置,把position设置到最后一个未读元素的后面。limit属性设置为capacity |
capacity() | 返回缓冲区大小 |
limit() | 返回limit上限的位置 |
mark() | 设置缓冲区的标志位置,这个值只能在0 ~ position之间,以后可以通过reset()方法返回到这个位置 |
position() | 可以返回position当前位置 |
remaining() | 返回当前position位置与limit之间的数据量 |
rewind() | 将position设置为0,取消mark标志位 |
clear() | 清空缓冲区,仅仅是修改position标志为0,设置limit为capacity,缓冲区数据还是存在的 |
flip() | 可以把缓冲区由写模式切换到读模式,先把limit设置为position位置,再把position设置为0 |
3、案例
1)、创建buffer缓冲区
package JavaNIO;import java.nio.CharBuffer;
import java.util.Arrays;/*缓冲区的创建方式1)、分配操作创建缓冲区,allocate()分配一个私有的、指定容量大小的数组来存储元素2)、包装操作创建缓冲区,它使用提供的数组作为存储空间来存储缓冲区中的数据,不再分配其他空间*/
public class CreadBuffer {public static void main(String[] args) {//1)、分配操作创建缓冲区CharBuffer buffer = CharBuffer.allocate(16);//2)、包装操作创建缓冲区char[] myarray = new char[16];//把已存在的数组包装成一个buffer对象CharBuffer buffer1 = CharBuffer.wrap(myarray);//通过调用put方法向缓冲区中保存数据,也会直接影响到数组buffer1.put("hello");buffer1.flip(); //切换到读模式System.out.println(buffer1);System.out.println(Arrays.toString(myarray));//对数组做的任何修改,也会影响缓冲区对象/*不管是allocate()还是wrap()创建的缓冲区都是间接的,间接缓冲区会使用备份数组,hasArray()方法可以判断是否有一个可存取的备份数组如果hasArray()返回true,可以通过array()返回缓冲区对象使用的备份数组的引用备份数组的内容跟原始数组的内容是一致的*/if (buffer1.hasArray()){char[] arr2 = buffer1.array();System.out.println(Arrays.toString(arr2));}}
}
2)、相关API
package JavaNIO;import java.nio.CharBuffer;public class Buffer {public static void main(String[] args) {//1、创建CharBuffer缓冲区对象CharBuffer buffer = CharBuffer.allocate(12);//2、打印capacity,limit,positionSystem.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:12,position:0//3、向缓冲区存储数据buffer.put('成');buffer.put('吉');buffer.put('思');buffer.put('汗');System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:12,position:4//4、调用filp()方法把缓冲区切换为读模式buffer.flip();System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:4,position:0//5、调用get()方法直接读取缓冲区数据System.out.println(buffer.get());System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:4,position:1//6、再次存储数据,把数据保存在position位置buffer.put('X');System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:4,position:2//7、设置标记buffer.mark();//8、再读取一个字符System.out.println(buffer.get());System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:4,position:3//9、调用reset(),把position重置为mark标记位置buffer.reset();System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//capacity:12,limit:4,position:2//10、调用compact()压缩,会把postion后的数据移动到起始位置,limit重置为capacitybuffer.compact();System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//11、调用clear清空buffer.clear();System.out.println("capacity:" + buffer.capacity()+ ",limit:" + buffer.limit() + ",position:" + buffer.position());//12、clear清空后,缓冲区的数据依然存在while (buffer.hasRemaining()){System.out.println(buffer.get());}}
}
3)、批量读取/写入
package JavaNIO;import java.nio.CharBuffer;
import java.util.Arrays;/*使用缓冲区就是为了提高数据传输效率,一次读写一个字符或一个字节效率并不高,可以进行批量的操作可以借助于数组,把缓冲区中的一块数据读取到数组中,也可以把数组中的部分内容保存到缓冲区中*/
public class BatchRead {public static void main(String[] args) {//创建CharBufferCharBuffer buffer = CharBuffer.allocate(15);buffer.put("云想衣裳花想容,春风拂槛露华浓");//切换为读模式buffer.flip();System.out.println(buffer);//定义字符数组char[] dst = new char[8];//调用get()方法把缓冲区中的数据读到字符数组中//注意批量传输时大小总是固定的,如果没有指定传输的大小,意味着把数组填满CharBuffer remainBuffer = buffer.get(dst);System.out.println(Arrays.toString(dst));//[云, 想, 衣, 裳, 花, 想, 容, ,],相当于把前8位写入字符数组中System.out.println(remainBuffer);//春风拂槛露华浓,后面再调用get()方法,剩余的就是remainBuffer//继续把buffer缓冲区的内容读到字符数组//buffer.get(dst);这时候是会报错的//当缓冲区的数据量不足以填满整个数组时,会抛出异常,剩下的是"春风拂槛露华浓",只有7个字符//此时可以调用get的重载,可以指定缓冲区长度buffer.get(dst,0,buffer.remaining());System.out.println(dst);}
}