前言
本来是想学习Netty的,但是Netty是一个NIO框架,因此在学习netty之前,还是先梳理一下NIO的知识。通过剖析源码理解NIO的设计原理。
本系列文章针对的是JDK1.8.0.161的源码。
上一篇对SocketChannel的源码进行了分析,本篇继续对FileChannel的源码进行解析。
RandomAccessFile
我们可以通过使用RandomAccessFile
读写数据。也可以通过FileInputStream
读数据或通过FileOutputStream
写数据。但实际这三个类内部实际是一样的,我们就以RandomAccessFile
为例子说明FileChannelImpl
的实现。
接口
RandomAccessFile
实现了DataInput
和DataOutput
两个接口,即数据输入和输出接口。
public class RandomAccessFile implements DataOutput, DataInput, Closeable {
}
DataInput
定义了一些基本的读取方法
- 读取指定长度的字节数据
- 读取数据并转换为基元类型。
- 读取一行数据。读取到
\r
会丢弃,读取到\n
会丢弃并停止继续读取。 - 用UTF-8编码读取一个string
public interface DataInput {
void readFully(byte b[]) throws IOException;
void readFully(byte b[], int off, int len) throws IOException;
int skipBytes(int n) throws IOException;
XXX readXXX() throws IOException;
String readLine() throws IOException;
String readUTF() throws IOException;
}
DataOutput
定义了一些基本的写方法
- 写入指定长度字节数据到文件。
- 将基元类型写入文件。
- 使用UTF-8编码写入一个string到文件。
public interface DataOutput {
void write(int b) throws IOException;
void write(byte b[]) throws IOException;
void write(byte b[], int off, int len) throws IOException;
void writeXXX(XXX v) throws IOException;
void writeUTF(String s) throws IOException;
}
创建实例
在创建RandomAccessFile我们需要传入两个参数:第一个是文件路径,第二个是文件访问方式。
public RandomAccessFile(String name, String mode)
throws FileNotFoundException
{
this(name != null ? new File(name) : null, mode);
}
public RandomAccessFile(File file, String mode)
throws FileNotFoundException
{
//File用于检查文件路径是否有效
String name = (file != null ? file.getPath() : null);
int imode = -1;
//判断文件访问方式
if (mode.equals("r"))
imode = O_RDONLY;
else if (mode.startsWith("rw")) {
imode = O_RDWR;
rw = true;
if (mode.length() > 2) {
if (mode.equals("rws"))
imode |= O_SYNC;
else if (mode.equals("rwd"))
imode |= O_DSYNC;
else
imode = -1;
}
}
if (imode < 0)
throw new IllegalArgumentException("Illegal mode \"" + mode + "\" must be one of \"r\", \"rw\", \"rws\", or \"rwd\"");
//检查读写权限
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
if (rw) {
security.checkWrite(name);
}
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name, imode);
}
- 首先会创建一个File对象,用于检查文件路径是否合法。目前仅检查文件路径是否含有
Nul
(/u0000)。 - 检查文件操作方式,文件有四种操作方式
模式 | 说明 |
---|---|
r | 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 |
rw | 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。 |
rwd | 打开以便读取和写入,这点和rw 的操作完全一致,但是只会在cache满或者调用RandomAccessFile.close()的时候才会执行内容同步操作。 |
rws | 在"rwd"的基础上对内容同步的要求更加严苛,每write修改一个byte都会直接修改到磁盘中。 |
- 创建
SecurityManager
检查读写文件权限 - 创建文件描述符
- 打开文件
获取文件通道
通过getChannel
可以获取文件通道,进行文件读写。
public final FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, true, rw, this);
}
return channel;
}
}
通过FileChannelImpl.open
创建一个FileChannelImpl实例。