Java IO/NIO多种读写文件方式
我们通常读写文件都是使用的阻塞模式,与之对应的也就是。java.io.FileInputStream
类提供了对文件的读取功能,Java的其他读取文件的方法基本上都是封装了java.io.FileInputStream
类,比如:java.io.FileReader
。
使用FileInputStream实现文件读取Demo:
输出结果如下:
##
# User Database
#
# Note that this file is consulted directly only when the system is running
# in single-user mode. At other times this information is provided by
# Open Directory.
#
# See the opendirectoryd(8) man page for additional information about
# Open Directory.
##
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
.....内容过长省去多余内容
调用链如下:
java.io.FileInputStream.readBytes(FileInputStream.java:219)
java.io.FileInputStream.read(FileInputStream.java:233)
com.anbai.sec.filesystem.FileInputStreamDemo.main(FileInputStreamDemo.java:27)
其中的readBytes是native方法,文件的打开、关闭等方法也都是native方法:
private native int readBytes(byte b[], int off, int len) throws IOException;
private native void open0(String name) throws FileNotFoundException;
private native int read0() throws IOException;
private native long skip0(long n) throws IOException;
private native int available0() throws IOException;
private native void close0() throws IOException;
java.io.FileInputStream
类对应的native实现如下:
使用FileOutputStream实现写文件Demo:
package com.anbai.sec.filesystem;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Creator: yz
* Date: 2019/12/4
*/
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
// 定义写入文件路径
File file = new File("/tmp/1.txt");
// 定义待写入文件内容
String content = "Hello World.";
// 创建FileOutputStream对象
FileOutputStream fos = new FileOutputStream(file);
fos.write(content.getBytes());
fos.flush();
fos.close();
}
}
代码逻辑比较简单: 打开文件->写内容->关闭文件,调用链和底层实现分析请参考FileInputStream
。
Java提供了一个非常有趣的读取文件内容的类: java.io.RandomAccessFile
,这个类名字面意思是任意文件内容访问,特别之处是这个类不仅可以像java.io.FileInputStream
一样读取文件,而且还可以写文件。
RandomAccessFile读取文件测试代码:
package com.anbai.sec.filesystem;
import java.io.*;
/**
* Creator: yz
* Date: 2019/12/4
*/
public class RandomAccessFileDemo {
public static void main(String[] args) {
File file = new File("/etc/passwd");
try {
// 创建RandomAccessFile对象,r表示以只读模式打开文件,一共有:r(只读)、rw(读写)、
// rws(读写内容同步)、rwd(读写内容或元数据同步)四种模式。
RandomAccessFile raf = new RandomAccessFile(file, "r");
// 定义每次输入流读取到的字节数对象
int a = 0;
// 定义缓冲区大小
byte[] bytes = new byte[1024];
// 创建二进制输出流对象
ByteArrayOutputStream out = new ByteArrayOutputStream();
// 循环读取文件内容
while ((a = raf.read(bytes)) != -1) {
// 截取缓冲区数组中的内容,(bytes, 0, a)其中的0表示从bytes数组的
// 下标0开始截取,a表示输入流read到的字节数。
out.write(bytes, 0, a);
}
System.out.println(out.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
任意文件读取特性体现在如下方法:
// 获取文件描述符
public final FileDescriptor getFD() throws IOException
public native long getFilePointer() throws IOException;
// 设置文件偏移量
private native void seek0(long pos) throws IOException;
java.io.RandomAccessFile
类中提供了几十个方法用以读取文件系统,最终都会调用到read0
或者readBytes
方法,我们只需要掌握如何利用RandomAccessFile
读/写文件就行了。
前面章节提到了JDK7新增的NIO.2的java.nio.file.spi.FileSystemProvider
,利用FileSystemProvider
我们可以利用支持异步的通道(Channel
)模式读取文件内容。
FileSystemProvider读取文件内容示例:
package com.anbai.sec.filesystem;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Creator: yz
* Date: 2019/12/4
*/
public class FilesDemo {
public static void main(String[] args) {
// 通过File对象定义读取的文件路径
// File file = new File("/etc/passwd");
// Path path1 = file.toPath();
// 定义读取的文件路径
Path path = Paths.get("/etc/passwd");
try {
byte[] bytes = Files.readAllBytes(path);
System.out.println(new String(bytes));
} catch (IOException e) {
e.printStackTrace();
}
}
}
java.nio.file.Files
是JDK7开始提供的一个对文件读写取非常便捷的API,其底层实在是调用了java.nio.file.spi.FileSystemProvider
来实现对文件的读写的。最为底层的实现类是sun.nio.ch.FileDispatcherImpl#read0
。
基于NIO的文件读取逻辑是:打开FileChannel->读取Channel内容。
打开FileChannel的调用链为:
sun.nio.ch.FileChannelImpl.<init>(FileChannelImpl.java:89)
sun.nio.ch.FileChannelImpl.open(FileChannelImpl.java:105)
sun.nio.fs.UnixChannelFactory.newFileChannel(UnixChannelFactory.java:137)
sun.nio.fs.UnixChannelFactory.newFileChannel(UnixChannelFactory.java:148)
sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:212)
java.nio.file.Files.newByteChannel(Files.java:361)
java.nio.file.Files.newByteChannel(Files.java:407)
java.nio.file.Files.readAllBytes(Files.java:3152)
com.anbai.sec.filesystem.FilesDemo.main(FilesDemo.java:23)
文件读取的调用链为:
sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:147)
sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:65)
sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:109)
sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:103)
java.nio.file.Files.read(Files.java:3105)
java.nio.file.Files.readAllBytes(Files.java:3158)
Java内置的文件读取方式大概就是这三种方式,其他的文件读取API可以说都是对这几种方式的封装而已(依赖数据库、命令执行、自写JNI接口不算,本人个人理解,如有其他途径还请告知)。本章我们通过深入基于IO和NIO的Java文件系统底层API,希望大家能够通过以上Demo深入了解到文件读写的原理和本质。