Java 文件操作
# 一、概述
文件和目录操作是每个编程语言中的基础功能。Java 提供了两套文件操作 API:
- 传统 I/O(
java.io.File
):Java 7 之前的主要方式,概念简单,使用方便 - NIO.2(
java.nio.file
):Java 7 引入,功能更强大,性能更好,API 更现代化
在开始之前,建议先了解 Java IO 的基础知识。
# 二、快速对比
功能 | 传统 I/O (File ) | NIO.2 (Path /Files ) |
---|---|---|
文件路径表示 | File 对象 | Path 对象 |
文件操作 | File 类方法 | Files 工具类 |
异常处理 | 返回布尔值 | 抛出具体异常 |
符号链接 | 不支持 | 原生支持 |
文件属性 | 基础属性 | 丰富的属性支持 |
监视服务 | 不支持 | WatchService |
异步操作 | 不支持 | 支持异步 I/O |
# 三、文件对象创建
# 1、传统方式(File)
// 创建文件对象
File file = new File("d:/test.txt");
File file2 = new File("d:/", "test.txt");
File file3 = new File(new File("d:/"), "test.txt");
// 创建目录对象
File dir = new File("d:/data");
// 判断类型
if (file.isFile()) {
System.out.println("这是一个文件");
}
if (dir.isDirectory()) {
System.out.println("这是一个目录");
}
// 常用判断方法
file.exists(); // 是否存在
file.isAbsolute(); // 是否绝对路径
file.isHidden(); // 是否隐藏
file.canRead(); // 是否可读
file.canWrite(); // 是否可写
file.canExecute(); // 是否可执行
# 2、2 方式(Path/Files)
// 创建路径对象
Path path = Paths.get("d:/test.txt");
Path path2 = Paths.get("d:/", "test.txt");
Path path3 = Path.of("d:/", "test.txt"); // Java 11+
// 创建目录路径
Path dir = Paths.get("d:/data");
// 判断类型
if (Files.isRegularFile(path)) {
System.out.println("这是一个文件");
}
if (Files.isDirectory(dir)) {
System.out.println("这是一个目录");
}
// 丰富的判断方法
Files.exists(path); // 是否存在
Files.notExists(path); // 是否不存在
Files.isReadable(path); // 是否可读
Files.isWritable(path); // 是否可写
Files.isExecutable(path); // 是否可执行
Files.isHidden(path); // 是否隐藏
Files.isSymbolicLink(path); // 是否符号链接
Files.isSameFile(path, path2); // 是否同一文件
# 3、与 Path 相互转换
// File 转 Path
Path path = new File("d:/test.txt").toPath();
// Path 转 File
File file = Paths.get("d:/test.txt").toFile();
# 四、获取文件信息
# 1、传统方式
File file = new File("d:/test.txt");
// 基本信息
String name = file.getName(); // 文件名
String absolutePath = file.getAbsolutePath(); // 绝对路径
String parent = file.getParent(); // 父目录路径
File parentFile = file.getParentFile(); // 父目录对象
long length = file.length(); // 文件大小(字节)
long lastModified = file.lastModified(); // 最后修改时间
// 规范路径(解析 . 和 .. 符号)
try {
String canonicalPath = file.getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
// 磁盘空间信息
long totalSpace = file.getTotalSpace(); // 总空间
long freeSpace = file.getFreeSpace(); // 剩余空间
long usableSpace = file.getUsableSpace(); // 可用空间
# 2、2 方式
Path path = Paths.get("d:/test.txt");
// 基本信息
Path fileName = path.getFileName(); // 文件名
Path parent = path.getParent(); // 父路径
Path root = path.getRoot(); // 根路径
int nameCount = path.getNameCount(); // 路径元素数量
// 文件大小
long size = Files.size(path);
// 规范化路径
Path normalized = path.normalize(); // 解析 . 和 ..
Path real = path.toRealPath(); // 获取真实路径
// 最后修改时间
FileTime lastModified = Files.getLastModifiedTime(path);
Instant instant = lastModified.toInstant();
# 五、文件属性操作
# 1、读取文件属性
Path path = Paths.get("d:/test.txt");
// 读取基本属性
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println("创建时间: " + attrs.creationTime());
System.out.println("最后修改: " + attrs.lastModifiedTime());
System.out.println("最后访问: " + attrs.lastAccessTime());
System.out.println("文件大小: " + attrs.size());
System.out.println("是否目录: " + attrs.isDirectory());
System.out.println("是否文件: " + attrs.isRegularFile());
// Windows 系统特有属性
if (System.getProperty("os.name").startsWith("Windows")) {
DosFileAttributes dosAttrs = Files.readAttributes(path, DosFileAttributes.class);
System.out.println("是否隐藏: " + dosAttrs.isHidden());
System.out.println("是否系统文件: " + dosAttrs.isSystem());
System.out.println("是否只读: " + dosAttrs.isReadOnly());
System.out.println("是否归档: " + dosAttrs.isArchive());
}
// Unix/Linux 系统特有属性
if (System.getProperty("os.name").contains("nix") ||
System.getProperty("os.name").contains("nux")) {
PosixFileAttributes posixAttrs = Files.readAttributes(path, PosixFileAttributes.class);
System.out.println("所有者: " + posixAttrs.owner());
System.out.println("组: " + posixAttrs.group());
System.out.println("权限: " + PosixFilePermissions.toString(posixAttrs.permissions()));
}
# 2、设置文件属性
Path path = Paths.get("d:/test.txt");
// 设置最后修改时间
FileTime newTime = FileTime.from(Instant.now());
Files.setLastModifiedTime(path, newTime);
// Windows 系统设置属性
if (System.getProperty("os.name").startsWith("Windows")) {
Files.setAttribute(path, "dos:hidden", true);
Files.setAttribute(path, "dos:readonly", true);
}
// Unix/Linux 系统设置权限
if (System.getProperty("os.name").contains("nix")) {
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r--r--");
Files.setPosixFilePermissions(path, perms);
// 设置所有者
UserPrincipal owner = path.getFileSystem()
.getUserPrincipalLookupService()
.lookupPrincipalByName("username");
Files.setOwner(path, owner);
}
# 3、探测文件类型
// 根据文件扩展名探测 MIME 类型
String mimeType = Files.probeContentType(Paths.get("test.jpg")); // image/jpeg
System.out.println("MIME 类型: " + mimeType);
// 常见文件类型
Files.probeContentType(Paths.get("test.txt")); // text/plain
Files.probeContentType(Paths.get("test.html")); // text/html
Files.probeContentType(Paths.get("test.pdf")); // application/pdf
Files.probeContentType(Paths.get("test.mp4")); // video/mp4
Files.probeContentType(Paths.get("test.xlsx")); // application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
// 注意:该方法基于文件扩展名,不检查实际内容
# 六、读写文件内容
# 1、小文件读写(一次性加载)
Path path = Paths.get("d:/test.txt");
// 读取文本文件
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
String content = Files.readString(path); // Java 11+
// 读取二进制文件
byte[] bytes = Files.readAllBytes(path);
// 写入文本文件
List<String> linesToWrite = Arrays.asList("第一行", "第二行", "第三行");
Files.write(path, linesToWrite, StandardCharsets.UTF_8);
Files.writeString(path, "文件内容"); // Java 11+
// 写入二进制文件
byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
Files.write(path, data);
// 追加内容
Files.write(path, linesToWrite, StandardCharsets.UTF_8,
StandardOpenOption.CREATE, // 文件不存在则创建
StandardOpenOption.APPEND); // 追加模式
# 2、大文件读写(流式处理)
Path path = Paths.get("d:/large-file.txt");
// 使用 BufferedReader 读取大文件
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
// 处理每一行
System.out.println(line);
}
}
// 使用 Stream API 读取(Java 8+)
try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
lines.filter(line -> line.contains("关键字"))
.forEach(System.out::println);
}
// 使用 BufferedWriter 写入大文件
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
for (int i = 0; i < 1000000; i++) {
writer.write("Line " + i);
writer.newLine();
}
}
// 使用输入输出流
try (InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(Paths.get("d:/output.txt"))) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
# 七、创建、复制、移动和删除
# 1、创建操作
// 创建文件
Path file = Paths.get("d:/new-file.txt");
Files.createFile(file); // 文件已存在会抛异常
// 安全创建文件(先检查)
if (!Files.exists(file)) {
Files.createFile(file);
}
// 创建目录
Path dir = Paths.get("d:/new-dir");
Files.createDirectory(dir); // 父目录必须存在
// 创建多级目录
Path dirs = Paths.get("d:/parent/child/grandchild");
Files.createDirectories(dirs); // 自动创建所有不存在的父目录
// 创建临时文件
Path tempFile = Files.createTempFile("prefix-", ".txt");
Path tempFileInDir = Files.createTempFile(Paths.get("d:/"), "temp-", ".txt");
// 创建临时目录
Path tempDir = Files.createTempDirectory("temp-dir-");
# 2、复制操作
Path source = Paths.get("d:/source.txt");
Path target = Paths.get("d:/target.txt");
// 基本复制
Files.copy(source, target);
// 复制并覆盖已存在的文件
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// 复制文件属性
Files.copy(source, target,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
// 复制目录(不包含内容)
Path sourceDir = Paths.get("d:/source-dir");
Path targetDir = Paths.get("d:/target-dir");
Files.copy(sourceDir, targetDir);
// 递归复制目录及其内容
Files.walk(sourceDir)
.forEach(sourcePath -> {
try {
Path targetPath = targetDir.resolve(sourceDir.relativize(sourcePath));
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
});
# 3、移动操作
Path source = Paths.get("d:/old-name.txt");
Path target = Paths.get("d:/new-name.txt");
// 移动/重命名文件
Files.move(source, target);
// 移动并覆盖
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
// 原子移动(要么完全成功,要么完全失败)
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
// 移动目录
Path sourceDir = Paths.get("d:/old-dir");
Path targetDir = Paths.get("e:/new-dir");
Files.move(sourceDir, targetDir, StandardCopyOption.REPLACE_EXISTING);
# 4、删除操作
Path file = Paths.get("d:/file-to-delete.txt");
// 删除文件(文件不存在会抛异常)
Files.delete(file);
// 安全删除(文件不存在不会抛异常)
Files.deleteIfExists(file);
// 删除目录(目录必须为空)
Path dir = Paths.get("d:/empty-dir");
Files.delete(dir);
// 递归删除目录及其内容
Path dirToDelete = Paths.get("d:/dir-to-delete");
Files.walk(dirToDelete)
.sorted(Comparator.reverseOrder()) // 先删除子文件/目录
.map(Path::toFile)
.forEach(File::delete);
// 使用 FileVisitor 递归删除
Files.walkFileTree(dirToDelete, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
# 八、目录遍历
# 1、传统方式
File dir = new File("d:/data");
// 获取文件名列表
String[] fileNames = dir.list();
// 使用过滤器
String[] txtFiles = dir.list((dir1, name) -> name.endsWith(".txt"));
// 获取文件对象列表
File[] files = dir.listFiles();
// 使用文件过滤器
File[] readableFiles = dir.listFiles(File::canRead);
File[] largeFiles = dir.listFiles(file -> file.length() > 1024 * 1024); // > 1MB
# 2、2 方式
Path dir = Paths.get("d:/data");
// 列出目录内容(非递归)
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path entry : stream) {
System.out.println(entry.getFileName());
}
}
// 使用过滤器
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) {
stream.forEach(System.out::println);
}
// 使用 Stream API(Java 8+)
try (Stream<Path> paths = Files.list(dir)) {
paths.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
// 递归遍历(深度优先)
try (Stream<Path> paths = Files.walk(dir)) {
paths.filter(Files::isRegularFile)
.forEach(System.out::println);
}
// 限制遍历深度
try (Stream<Path> paths = Files.walk(dir, 2)) { // 最多遍历2层
paths.forEach(System.out::println);
}
// 查找文件
try (Stream<Path> paths = Files.find(dir, Integer.MAX_VALUE,
(path, attrs) -> attrs.isRegularFile() && path.toString().endsWith(".txt"))) {
paths.forEach(System.out::println);
}
# 3、使用 FileVisitor 模式
Path startDir = Paths.get("d:/project");
Files.walkFileTree(startDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
System.out.println("进入目录: " + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("访问文件: " + file);
if (file.toString().endsWith(".class")) {
// 跳过 .class 文件的子目录
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.println("访问失败: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
System.out.println("离开目录: " + dir);
return FileVisitResult.CONTINUE;
}
});
# 九、路径操作
# 1、路径解析和组合
Path base = Paths.get("/home/user");
// 解析子路径
Path resolved = base.resolve("documents/file.txt"); // /home/user/documents/file.txt
// 解析兄弟路径
Path sibling = base.resolveSibling("guest"); // /home/guest
// 获取相对路径
Path p1 = Paths.get("/home/user/docs");
Path p2 = Paths.get("/home/user/downloads/files");
Path relative = p1.relativize(p2); // ../downloads/files
// 规范化路径(移除 . 和 ..)
Path path = Paths.get("/home/./user/../user/docs");
Path normalized = path.normalize(); // /home/user/docs
// 转换为绝对路径
Path relativePath = Paths.get("../file.txt");
Path absolutePath = relativePath.toAbsolutePath();
// 获取真实路径(解析符号链接)
Path realPath = path.toRealPath();
# 2、路径匹配
Path path = Paths.get("/home/user/documents/report.pdf");
// 使用 PathMatcher
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.pdf");
boolean matches = matcher.matches(path.getFileName()); // true
// 支持的语法
PathMatcher globMatcher = FileSystems.getDefault().getPathMatcher("glob:**/*.{java,class}");
PathMatcher regexMatcher = FileSystems.getDefault().getPathMatcher("regex:.*\\.txt");
// 在遍历中使用
try (Stream<Path> paths = Files.walk(Paths.get("."))) {
paths.filter(globMatcher::matches)
.forEach(System.out::println);
}
# 十、随机访问
RandomAccessFile
支持对文件的随机读写访问,核心是 seek()
方法定位文件指针。
// 读取文件的特定部分
try (RandomAccessFile raf = new RandomAccessFile("d:/large-file.dat", "r")) {
// 跳到第 1024 字节
raf.seek(1024);
// 读取 100 字节
byte[] buffer = new byte[100];
raf.read(buffer);
// 获取当前文件指针位置
long position = raf.getFilePointer();
}
// 在文件末尾追加内容
try (RandomAccessFile raf = new RandomAccessFile("d:/log.txt", "rw")) {
// 移到文件末尾
raf.seek(raf.length());
// 追加内容
raf.writeBytes("\n新的日志条目");
}
// 实现简单的文件分块读取
public void readFileInChunks(String filename, int chunkSize) throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
long fileSize = raf.length();
long chunks = (fileSize + chunkSize - 1) / chunkSize;
for (long i = 0; i < chunks; i++) {
raf.seek(i * chunkSize);
byte[] buffer = new byte[chunkSize];
int bytesRead = raf.read(buffer);
// 处理读取的数据
processChunk(buffer, bytesRead);
}
}
}
访问模式说明:
"r"
:只读模式"rw"
:读写模式,文件不存在会创建"rws"
:读写模式,每次写入同步内容和元数据"rwd"
:读写模式,每次写入只同步内容
# 十一、文件监视服务
NIO.2 提供了 WatchService
来监视文件系统的变化:
// 创建监视服务
WatchService watchService = FileSystems.getDefault().newWatchService();
// 注册要监视的目录
Path dir = Paths.get("d:/watched-dir");
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
// 处理文件系统事件
while (true) {
WatchKey key;
try {
key = watchService.take(); // 阻塞等待事件
} catch (InterruptedException e) {
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();
System.out.printf("事件 %s: %s%n", kind.name(), filename);
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
# 十二、实战示例
# 1、文件备份工具
public class FileBackup {
public static void backupFile(Path source) throws IOException {
// 生成备份文件名
String timestamp = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
String fileName = source.getFileName().toString();
String backupName = fileName.replaceFirst("(\\.[^.]+)?$", "_" + timestamp + "$1");
Path backup = source.resolveSibling(backupName);
// 复制文件并保留属性
Files.copy(source, backup,
StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING);
System.out.println("备份创建: " + backup);
}
}
# 2、目录同步工具
public class DirectorySync {
public static void syncDirectories(Path source, Path target) throws IOException {
// 确保目标目录存在
Files.createDirectories(target);
// 遍历源目录
Files.walk(source).forEach(sourcePath -> {
try {
Path targetPath = target.resolve(source.relativize(sourcePath));
if (Files.isDirectory(sourcePath)) {
Files.createDirectories(targetPath);
} else {
// 只复制较新的文件
if (!Files.exists(targetPath) ||
Files.getLastModifiedTime(sourcePath).compareTo(
Files.getLastModifiedTime(targetPath)) > 0) {
Files.copy(sourcePath, targetPath,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
System.out.println("同步: " + targetPath);
}
}
} catch (IOException e) {
System.err.println("同步失败: " + sourcePath);
}
});
}
}
# 3、文件搜索工具
public class FileSearch {
public static List<Path> searchFiles(Path startDir, String pattern) throws IOException {
List<Path> result = new ArrayList<>();
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
Files.walkFileTree(startDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (matcher.matches(file.getFileName())) {
result.add(file);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.println("无法访问: " + file);
return FileVisitResult.CONTINUE;
}
});
return result;
}
// 使用示例
public static void main(String[] args) throws IOException {
List<Path> javaFiles = searchFiles(Paths.get("."), "*.java");
javaFiles.forEach(System.out::println);
}
}
# 4、大文件分割与合并
public class FileSplitter {
// 分割文件
public static void splitFile(Path source, int chunkSize) throws IOException {
byte[] buffer = new byte[chunkSize];
try (InputStream input = Files.newInputStream(source)) {
int partNumber = 1;
int bytesRead;
while ((bytesRead = input.read(buffer)) > 0) {
String partName = source.getFileName() + ".part" + partNumber++;
Path partFile = source.resolveSibling(partName);
Files.write(partFile, buffer, 0, bytesRead);
System.out.println("创建分片: " + partFile);
}
}
}
// 合并文件
public static void mergeFiles(Path targetFile, Path... parts) throws IOException {
try (OutputStream output = Files.newOutputStream(targetFile,
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
for (Path part : parts) {
Files.copy(part, output);
System.out.println("合并: " + part);
}
}
}
}
# 十三、工具类库
# 1、Commons IO
Commons IO 提供了许多实用的文件操作工具:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
常用功能示例:
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.*;
// 文件操作
File file = new File("d:/test.txt");
// 读写文件
String content = FileUtils.readFileToString(file, "UTF-8");
FileUtils.writeStringToFile(file, "内容", "UTF-8");
List<String> lines = FileUtils.readLines(file, "UTF-8");
// 复制文件/目录
FileUtils.copyFile(srcFile, destFile);
FileUtils.copyDirectory(srcDir, destDir);
FileUtils.copyDirectoryToDirectory(srcDir, destDir);
// 移动文件/目录
FileUtils.moveFile(srcFile, destFile);
FileUtils.moveDirectory(srcDir, destDir);
// 删除文件/目录
FileUtils.deleteQuietly(file); // 静默删除
FileUtils.deleteDirectory(directory); // 递归删除目录
FileUtils.cleanDirectory(directory); // 清空目录
// 创建目录
FileUtils.forceMkdir(directory); // 强制创建目录,包括父目录
// 文件大小
long size = FileUtils.sizeOf(file);
String displaySize = FileUtils.byteCountToDisplaySize(size); // 如: "1.2 GB"
// 文件比较
boolean isEqual = FileUtils.contentEquals(file1, file2);
// 文件过滤
Collection<File> files = FileUtils.listFiles(
directory,
new SuffixFileFilter(".java"),
TrueFileFilter.INSTANCE
);
// 文件名操作
String extension = FilenameUtils.getExtension("file.txt"); // "txt"
String baseName = FilenameUtils.getBaseName("file.txt"); // "file"
String path = FilenameUtils.getFullPath("/home/user/file.txt"); // "/home/user/"
# 十四、性能优化建议
# 1、选择合适的 API
// 小文件(< 10MB):一次性读取
String content = Files.readString(path);
// 中等文件(10MB - 100MB):使用缓冲流
try (BufferedReader reader = Files.newBufferedReader(path)) {
// 逐行处理
}
// 大文件(> 100MB):使用内存映射或分块读取
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel()) {
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_ONLY, 0, channel.size());
}
# 2、使用合适的缓冲区大小
// 默认缓冲区通常是 8KB,对于大文件可以增大
int bufferSize = 64 * 1024; // 64KB
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(file), bufferSize)) {
// 处理
}
# 3、批量操作优化
// 批量删除文件时,使用并行流
Files.walk(directory)
.parallel()
.filter(Files::isRegularFile)
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
// 处理异常
}
});
# 4、避免频繁的文件系统调用
// 缓存文件属性,避免重复调用
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
long size = attrs.size();
FileTime lastModified = attrs.lastModifiedTime();
boolean isDirectory = attrs.isDirectory();
# 十五、异常处理最佳实践
# 1、使用 try-with-resources
// 自动关闭资源
try (Stream<Path> paths = Files.walk(directory);
BufferedWriter writer = Files.newBufferedWriter(output)) {
paths.filter(Files::isRegularFile)
.map(Path::toString)
.forEach(line -> {
try {
writer.write(line);
writer.newLine();
} catch (IOException e) {
// 处理写入异常
}
});
}
# 2、具体的异常处理
try {
Files.createDirectory(path);
} catch (FileAlreadyExistsException e) {
// 文件已存在
System.err.println("目录已存在: " + path);
} catch (NoSuchFileException e) {
// 父目录不存在
System.err.println("父目录不存在: " + path.getParent());
} catch (AccessDeniedException e) {
// 权限不足
System.err.println("权限不足: " + path);
} catch (IOException e) {
// 其他 I/O 错误
System.err.println("创建目录失败: " + e.getMessage());
}
# 3、防御性编程
public static void safeDelete(Path path) {
try {
if (Files.exists(path)) {
if (Files.isDirectory(path)) {
// 确保目录为空
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
if (stream.iterator().hasNext()) {
System.err.println("目录不为空: " + path);
return;
}
}
}
Files.delete(path);
}
} catch (IOException e) {
System.err.println("删除失败: " + path + ", 原因: " + e.getMessage());
}
}
# 十六、总结
Java 文件操作提供了两套 API 体系:
传统 I/O(
File
):- 简单直观,适合基础操作
- 广泛使用,兼容性好
- 功能有限,错误处理较弱
NIO.2(
Path
/Files
):- 功能强大,支持更多特性
- 更好的异常处理
- 支持异步操作和文件监视
- 推荐在新项目中使用
选择建议:
- 简单操作:使用传统 I/O
- 复杂需求:使用 NIO.2
- 大文件处理:考虑
RandomAccessFile
或内存映射 - 跨平台:注意系统差异,做好兼容处理
记住:文件操作涉及系统资源,务必正确关闭资源,处理好异常!
祝你变得更强!
编辑 (opens new window)
上次更新: 2025/08/15