文件是 Java 应用程序中常见的数据源或目的地。因此,本文将简要介绍如何在 Java 中处理文件。我们无意在此详细解释每种技术,而是希望为你提供足够的知识来决定文件访问方法。
Java IO API 包含以下与在 Java 中处理文件有关的类:
File:表示文件或目录的抽象路径名。
RandomAccessFile:支持随机读写文件的类。
FileInputStream:用于从文件读取字节的流。
FileReader:用于从文件读取字符的流。
FileOutputStream:用于向文件写入字节的流。
FileWriter:用于向文件写入字符的流。
下文将简要介绍这些类。
API 文档地址:https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html
如果需要读取文件内容,可以使用 FileInputStream 或 FileReader 类。具体选择哪一个,取决于你想读取的是二进制数据还是文本数据。这两个类可以让你从文件中一次读取一个字节(byte)或字符(char),或者将字节/字符读入到一个字节/字符数组。
注意,这两个类读取文件必须从文件开头,逐一读取到文件末尾,不能随机读取。如下图:

其实,你可以将文件内容想象成一个大的字节或字符数组,我们读取文件时只能从下标为 0 的位置开始遍历。
示例:
package com.hxstrive.java_io.inputstream;
import java.io.FileInputStream;
/**
* 读取文件示例
* @author hxstrive.com
*/
public class FileInputStreamDemo1 {
public static void main(String[] args) {
try(FileInputStream fileInputStream = new FileInputStream("D:\\input.txt")) {
byte[] bytes = new byte[fileInputStream.available()];
int len = fileInputStream.read(bytes);
System.out.println(len); // 13
System.out.println(new String(bytes)); // Hello Java IO
} catch (Exception e) {
e.printStackTrace();
}
}
}如果需要在文件中跳转(即跳过 N 个字节或字符),只从这里或那里读取部分内容,可以使用 RandomAccessFile(随机读取文件类)。
如果需要将数据写入文件,可以使用 FileOutputStream 或 FileWriter 类,具体选择哪一个类取决于需要写入的是二进制数据还是字符。你可以每次写入一个字节(byte)或字符(char),也可以写入字节和字符数组。
注意:
(1)数据会按照写入的顺序依次存储在文件中。
(2)仅支持从文件开头一次写入到文件末尾,不能随机将字节或字符写入到文件中指定的位置。
示例:
package com.hxstrive.java_io.outputstream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* 写文件示例
* @author hxstrive.com
*/
public class FileOutputStreamDemo {
public static void main(String[] args) {
try(FileOutputStream output = new FileOutputStream("D:\\output.txt");
FileInputStream input = new FileInputStream("D:\\output.txt")) {
// 写出数据
output.write("Hello World!".getBytes());
output.flush();
// 读取数据
byte[] buffer = new byte[1024];
int len = input.read(buffer);
System.out.println(new String(buffer, 0, len)); // Hello World!
} catch (Exception e) {
e.printStackTrace();
}
}
}如果需要跳过文件并在不同位置写入数据,例如追加到文件末尾,可以使用 RandomAccessFile。
如前所述,你可以通过 RandomAccessFile 类使用 Java IO 随机访问文件。
随机存取并不意味着可以从真正随机的地方读取或写入。它只是意味着你可以跳过文件,以任何你想要的方式同时读取或写入文件。这就是“随机”的含义 —— 下一个字节的读取并不取决于前一个字节的读取,不强制执行特定的访问顺序。你可以 “随机”—— 任意地访问文件中的字节。这样,就可以覆盖现有文件的某些部分、向文件添加内容、删除文件,当然也可以从需要读取文件的地方读取文件。
如下图,从文件两个位置读取内容,先从文件末尾读取“位置1”对应的内容,然后再次从文件“位置2”读取对应的内容:

示例:
package com.hxstrive.java_io.other;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 随机访问文件
* @author hxstrive
*/
public class RandomAccessFileDemo {
public static void main(String[] args) {
String fileName = "D:\\random.txt";
try (RandomAccessFile file = new RandomAccessFile(fileName, "rw")) {
// 写入数据
file.writeUTF("Hello, World!");
// 将文件指针移到文件开头
file.seek(0);
// 读取数据
System.out.println("读取的数据: " + file.readUTF()); // 读取的数据: Hello, World!
// 修改数据
file.seek(7); // 将文件指针移到第 8 个字符的位置
file.writeUTF("Java");
// 将文件指针移到文件开头
file.seek(0);
// 再次读取数据
System.out.println("修改后的数据: " + file.readUTF()); // 修改后的数据: Hello Javad!
} catch (IOException e) {
e.printStackTrace();
}
}
}有时,你可能需要访问文件的相关信息而不是其内容。例如,你需要知道文件大小或文件属性。目录的情况也是如此。例如,你可能想获得一个给定目录中所有文件的列表。文件和目录信息都可以通过文件类获取。
示例:
package com.hxstrive.java_io.file;
import java.io.File;
/**
* 获取文件基本信息
* @author hxstrive.com
*/
public class FileDemo {
public static void main(String[] args) {
File file = new File("D:\\input.txt");
System.out.println("文件名:" + file.getName()); // 文件名:input.txt
System.out.println("文件路径:" + file.getPath()); // 文件路径:D:\input.txt
System.out.println("存在?" + file.exists()); // 存在?true
System.out.println("是文件?" + file.isFile()); // 是文件?true
System.out.println("是目录?" + file.isDirectory()); // 是目录?false
System.out.println("文件长度:" + file.length()); // 文件长度:13
System.out.println("最后修改日期:" + file.lastModified()); // 最后修改日期:1735194454080
System.out.println("能读?" + file.canRead()); // 能读?true
System.out.println("能写?" + file.canWrite()); // 能写?true
System.out.println("能执行?" + file.canExecute()); // 能执行?true
}
}递归访问一个目录下面所有到文件,例如:
package com.hxstrive.java_io.file;
import java.io.File;
import java.util.Objects;
/**
* 递归遍历目录
* @author hxstrive.com
*/
public class FileDemo2 {
public static void main(String[] args) {
File file = new File("D:\\demo");
if(!file.isDirectory()) {
// 不是目录
System.out.println(file.getAbsoluteFile());
return;
}
// 是目录
File[] files = file.listFiles();
if(Objects.nonNull(files)) {
show(file);
}
}
/**
* 递归显示目录内容
* @param file 文件/目录
*/
private static void show(File file) {
if(file.isDirectory()) {
File[] files = file.listFiles();
if(Objects.nonNull(files)) {
for(File f : files) {
if(f.isDirectory()) {
System.out.println(f.getAbsoluteFile());
show(f);
} else {
System.out.println(f.getAbsoluteFile());
}
}
}
} else {
System.out.println(file.getAbsoluteFile());
}
}
}使用 tree D:/demo /F /A 命令查看 demo 目录到结构,如下图:

运行结果如下:
D:\demo\c++ D:\demo\c++\Hello.c D:\demo\Hello.java D:\demo\java D:\demo\java\network D:\demo\java\network\Echo.java D:\demo\java\Test.java