阅读nio文档

This commit is contained in:
asahi
2024-08-25 23:38:56 +08:00
parent 19ce2bfe4f
commit 3c3ab1f41e

View File

@@ -36,3 +36,123 @@ buffer的本质是一个字节数组buffer提供了对数据的结构化访
上述的每个Buffer类都实现了Buffer接口除了ByteBuffer之外其他每个Buffer类型都拥有相同的操作。由于ByteBuffer被绝大多数标准io操作锁使用故而ByteBuffer除了拥有上述操作外还拥有一些额外的操作。
#### Buffer设计
Buffer是存储指定primitive type数据的容器Long,Byte,Int,Float,Char,Double,Shortbuffer是线性并且有序的。
buffer的主要属性包括capacity、limit和position
- capacitybuffer的capacity是其可以包含的元素数量capacity不可以为负数并且不可以被改变
- limitbuffer的limit代表buffer中第一个不应该被读/写的元素下表,`例如buffer的capacity为5但是其中只包含3个元素那么limit则为3`limit不能为负数并且limit不可以比capacity大
- positionposition代表下一个被读/写元素的下标position部为负数也不应该比limit大
#### 传输数据
buffer的每个子类都定义了get/put类似的操作相应的read或write操作会从position转移一个或多个元素的数据如果请求转移的数据比limit大那么相应的get操作会抛出`BufferUnderflowException`set操作则是会抛出`BufferOverflowException`;在上述两种情况下,没有数据会被传输。
#### mark & reset
buffer的mark操作标识对buffer执行reset操作后position会被重置到的位置。mark并非所有情况下都被定义当mark被定义时其应该不为负数并且不应该比position大。如果position或limit被调整为比mark更小的值之后mark值将会被丢弃。如果在mark没有被定义时调用reset操作那么会抛出`InvalidMarkException`异常。
> #### 属性之间的大小关系
>
> `0<=mark<=position<=limit<=capacity`
#### clear/flip/rewind
除了对buffer定义mark/reset操作之外buffer还定义了如下操作
- `clear()`使buffer可以执行新的sequence channel-read操作或relative put操作
- clear操作会将limit设置为capacity的值并且将position设置为0
- `flip()`使buffer可以执行sequence channel-wirte操作和relative get操作
- flip操作会将limit设置为position并且将position设置为0
- `rewind()`使buffer中的数据可以重新被读取
- rewind操作会将position设置为0limit保持不变
> 在向buffer写入数据之前应该调用`clear()`方法将limit设置为capacity并将position设置为0从而可以向[0, capacity-1]的范围内写入数据
>
> 当写入完成之后想要从buffer中读取数据时需要先调用`flip()`方法将limit设置为position并且将position设置为0从而可以读取[0, position-1]范围内的数据
>
> 如果想要对buffer中的数据进行重新读取可以调用`rewind()`方法其将position设置为0从而可以对[0, limit-1]范围内的数据重新进行读取。
### Channel
Channel为一个对象可以从channel中读取数据或向channel写入数据。将传统io和nio相比较channel类似于stream。
Channel和Stream不同的是channel为双向的而Stream则仅支持单项InputStream或OutputStream。一个开启的channel可以被用于读取、写入或同时读取或写入。
## Channel读写
向channel读取或写入数据十分简单
-仅需创建一个buffer对象并且访问channel将数据读取到buffer对象中
-创建一个buffer对象并将数据填充到buffer对象之后访问channel并将bufffer中的数据写入到channel中
### 读取文件
通过nio读取文件分为如下步骤
1. 从FileInputStream中获取Channel
2. 创建buffer对象
3. 将channel中的数据读取到buffer
示例如下所示:
```java
FileInputStream fin = new FileInputStream( "readandshow.txt" );
// 1. 获取channel
FileChannel fc = fin.getChannel();
// 2. 创建buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 3. 将数据从channel中读取到buffer中
fc.read( buffer );
```
> 如上述示例所示在从channel读取数据时无需指定向buffer中读取多少字节的数据每个buffer内部中都有一个复杂的统计系统会跟踪已经读取了多少字节的数据、buffer中还有多少空间用于读取更多数据。
### 写入文件
通过nio写入文件步骤和读取类似示例如下所示
```java
FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
// 1. 获取channel
FileChannel fc = fout.getChannel();
// 2. 创建buffer对象
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i = 0; i < message.length; ++i) {
buffer.put(message[i]);
}
buffer.flip();
// 3. 将buffer中的数据写入到channel中
fc.write(buffer);
```
在向channel写入数据时同样无需指定需要向channel写入多少字节的数据buffer内部的统计系统同样会追踪buffer中含有多少字节的数据、还有多少数据待写入。
### 同时读取和写入文件
同时读取和写入文件的示例如下所示:
```java
public class CopyFile {
static public void main(String args[]) throws Exception {
if (args.length < 2) {
System.err.println("Usage: java CopyFile infile outfile");
System.exit(1);
}
String infile = args[0];
String outfile = args[1];
try (FileInputStream fin = new FileInputStream(infile);
FileOutputStream fout = new FileOutputStream(outfile)) {
FileChannel fcin = fin.getChannel();
FileChannel fcout = fout.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
buffer.clear();
int r = fcin.read(buffer);
if (r == -1) {
break;
}
buffer.flip();
fcout.write(buffer);
}
}
}
}
```
在上述示例中,通过`channel.read(buffer)`的返回值来判断是否已经读取到文件末尾,如果返回值为-1那么代表文件读取已经完成。