Java NIO
概念
Java NIO(New IO),No Blocking IO 非阻塞IO,是从Java1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的,基于通道的IO操作。NIO将以更加高效的方式进行文件读写操作
IO和NIO的区别
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向缓冲区(Buffer Oriented) |
阻塞IO(Blocking IO) | 非阻塞IO(Non Blocking IO) |
无 | 选择器(Selectors) |
- 传统的IO是单向的
- 也就是需要建立输入流和输出流两个管道,数据的流动只能是单向的
-
NIO是双向的
- 里面的缓存区是可以双向传输的
-
NIO里面引入的通道的概念
- 通道可以理解为我们生活中的铁路,它是用于源地址和目的地址连接的
- 如果需要实际传输的话,那么需要依赖里面的缓冲区
- 通道负责连接,缓冲区负责传输
通道和缓冲区
Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理
简而言之:Channel负责传输,Buffer负责存储
缓冲区 Buffer
在Java NIO中负责数据的存取。缓冲区就是数组。用于存储不同类型的数据根据数据类型不同,提供相同类型的缓冲区(除了Boolean)
- ByteBuffer:字节缓冲区(最常用的)
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
缓冲区中的方法
上面缓冲区的管理方式几乎一致, 通过 allocate() 获取缓冲区
缓冲区存取数据的两个核心方法
- put():存入数据到缓冲区中
- get():获取缓冲区中的数据
- hasRemaining():判断缓冲区是否还有剩余的数据
- remaining():获取缓冲区还有多少剩余数据
- mark():标记postion的位置
- reset():恢复到刚标记的地方
缓冲区中的核心属性
- capacity:容量,表示缓冲区中最大存储数据的容量,一旦申明不可改变。
- limit:界限,表示缓冲区中的可以操作数据的大小。(limit 后数据不能进行读写)
- position:位置,表示缓冲区中正在操作的位置
- mark:标记,表示记录当前 position 的位置,可以通过reset() 恢复到 mark的位置
最后它们之间的关系是:0 <= mark <= position <= limit <= capacity
相关操作
我们首先操作一个大小为1024字节的缓冲区ByteBuffer
// 分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("初始化");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
然后在传入字符串到缓冲区
// 存入数据到缓冲区
String str = "abcde";
buf.put(str.getBytes());
System.out.println("存入数据");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
然后开始读取数据,在读取数据前,我们需要使用flip切换到读取数据模式
// 切换读取数据模式
buf.flip();
System.out.println("切换读取数据模式");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
然后在进行读取操作,我们需要创建一个byte[] 数组,将需要读取出来的数据放进去
// 开始读取数据
System.out.println("开始读取数据");
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst, 0, dst.length));
下面这个图,是我们在执行各个步骤时, position,limit,capacity的变换
完整代码:
/**
* 缓冲区:Buffer
* 在Java NIO中负责数据的存取。缓冲区就是数组。用于存储不同类型的数据
* 根据数据类型不同,提供相同类型的缓冲区(除了Boolean)
* ByteBuffer
* CharBuffer
* @author: 陌溪
* @create: 2020-03-27-14:48
*/
public class BufferDemo {
public static void main(String[] args) {
// 分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("初始化");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
// 存入数据到缓冲区
String str = "abcde";
buf.put(str.getBytes());
System.out.println("存入数据");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
// 切换读取数据模式
buf.flip();
System.out.println("切换读取数据模式");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
// 开始读取数据
System.out.println("开始读取数据");
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst, 0, dst.length));
System.out.println("数据读取完毕");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
// rewind():表示重复读
buf.rewind();
System.out.println("rewind");
System.out.println("position:" + buf.position());
System.out.println("limit:" + buf.limit());
System.out.println("capacity:" + buf.capacity());
// clear():清空缓冲区,但是缓冲区中的数据仍然存储,但是处于被遗忘状态
buf.clear();
System.out.println("clear");
System.out.println("position:" + buf.position