Java NIO 概述
Java NIO 是 Java4 之后提供的一种带缓冲区非阻塞 IO 且是双向通信的, 数据是从通道到缓冲区, 或从缓冲区到通道
它由 ChannelBufferSelector 这几个部分构成了核心的 API
Channel(通道)
Channel 类似流, 但又有些不同:
既可以从通道中读取数据, 又可以写数据到通道但流的读写通常是单向的
通道可以异步地读写
通道中的数据总是要先读到一个 Buffer, 或者总是要从一个 Buffer 中写入
Channel 是通道的高层接口, 其有几个实现类:
FileChannel: 从文件中读写数据, 但无法设置为非阻塞模式
DatagramChannel: 能通过 UDP 读写网络中的数据
SocketChannel: 能通过 TCP 读写网络中的数据
ServerSocketChannel: 可以监听新进来的 TCP 连接, 像 web 服务器那样对每一个新进来的连接都会创建一个 SocketChannel
通道间的数据传输
FileChannel 的 transferFrom() 方法可以将数据从源通道传输到 FileChannel 中
- try (RandomAccessFile raf = new RandomAccessFile("/Users/linyuan/Documents / 字目录. txt", "rw");
- RandomAccessFile toFile = new RandomAccessFile("/Users/linyuan/Documents/toFile.txt", "rw")) {
- FileChannel fromChannel = raf.getChannel();
- FileChannel toChannel = toFile.getChannel();
- long position = 0;
- long count = fromChannel.size();
- // position 表示从 position 处开始向目标文件写入数据, count 表示最多传输的字节数
- // 如果源通道的剩余空间小于 count 个字节, 则所传输的字节数要小于请求的字节数
- toChannel.transferFrom(fromChannel, position, count);
- } catch (Exception e) {
- e.printStackTrace();
- }
transferTo() 方法将数据从 FileChannel 传输到其他的 channel 中
Buffer(缓冲区)
缓冲区本质上是一块可以读写数据的内存, 这块内存被包装成了 NIO 的 Buffer 对象, 并提供一组方法, 用来方便的访问该块内存
数据是从通道读入缓冲区, 从缓冲区写入到通道中的
Buffer 是缓冲区的高层接口, 其有几个实现类:
- ByteBufferMappedByteBufferCharBufferDoubleBuffer
- FloatBufferIntBufferLongBufferShortBuffer
通过 allocate 方法分配一个指定大小的 Buffer
- ByteBuffer buf = ByteBuffer.allocate(48);
- CharBuffer buf = CharBuffer.allocate(1024);
向 Buffer 中写数据有两种方式:
从 Channel 写到 Buffer
int bytesRead = inChannel.read(buf);
通过 put 方法写到 Buffer
buf.put(127);
向 Buffer 中读数据有两种方式:
从 Buffer 读取数据到 Channel
int bytesWritten = inChannel.write(buf);
通过 get 方法从 Buffer 中读取数据
byte aByte = buf.get();
常用方法:
flip(): 将 Buffer 从写模式切换到读模式
rewind(): 将 position 设回 0, 所以可以重读 Buffer 中所有数据
clear(): 清空缓冲区, 将 position 设回 0, 但实际上 Buffer 中的数据并未被清除, 只是标记回 0 后新写入的数据会覆盖原来的数据
compact(): 清除已读过的数据, 未读的数据会被移至缓冲区起始处, 新写入的数据将从缓冲区未读数据后面写入
mark(): 标记 Buffer 中一个特定的 position, 之后可通过 reset() 方法恢复到这个 position
equals(): 比较两个 Buffer 是否相同
它必须有相同的类型(intbyte 等)
Buffer 中剩余的 bytechar 等个数相同
Buffer 中所有剩余的 bytechar 等都相同
compareTo(): 比较两个 Buffer 大小, 如满足以下条件则认为一个 Buffer 小于另一个 Buffer
第一个不相等的元素小于另一个 Buffer 中对应的元素
所有元素都相等, 第一个 Buffer 剩余的空间小于第二个 Buffer
Buffer 的 capacity,position 和 limit
Buffer 包含了三个属性:
capacity:
代表 Buffer 的容量大小
一旦 Buffer 满了就需要将其清空 (通过读取数据或清除数据) 才能继续往缓冲区里写数据
position:
当写数据到 Buffer 中时 position 表示当前的位置, 初始值为 0, 最大值为 capacity - 1, 每写入一个 bytelong 等数据时 position 会下移到下一个可写的 Buffer 单元
当切换 Buffer 为读数据时, position 会被重置为 0, 每读入一个 bytelong 等数据时下移到下一个可读的位置
limit:
在写模式下, limit 表示最多能写入多少数据, 等于 capacity
在读模式下, limit 表示最多能读到多少数据, 等于于写模式下的 position 值
示例
使用 Buffer 读写数据一般遵循以下四个步骤:
写入数据到 Buffer
调用 flip() 方法将写模式切换到读模式
从 Buffer 中读取数据
调用 clear() 方法或者 compact() 方法清空缓冲区
可以通过 RandomAccessFile 对象的 getChannel 方法获取 Channel, 因为数据无论如何变化终究还是以字节形式存储
- try (RandomAccessFile raf = new RandomAccessFile("/Users/linyuan/Documents / 字目录. txt", "rw")) {
- // 获取通道 Channel
- FileChannel fileChannel = raf.getChannel();
- // 创建缓冲区 Buffer
- ByteBuffer byteBuffer = ByteBuffer.allocate(52);
- // 将通道的数据读入缓冲区
- int bytesRead = fileChannel.read(byteBuffer);
- while (bytesRead != -1) {
- // 切换缓冲区模式
- byteBuffer.flip();
- // 读取缓冲区字节
- while (byteBuffer.hasRemaining()) {
- System.out.print((char) byteBuffer.get());
- }
- byteBuffer.clear();
- bytesRead = fileChannel.read(byteBuffer);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
来源: http://www.jianshu.com/p/6814a0d619fd