Java NIO.2(New I/O 2)是 Java 7 引入的全新文件系统 API,位于 java.nio.file 包中。
它提供了比传统 java.io.File 更强大、更灵活的文件操作功能。

核心组件概览
1. 主要类和接口
类/接口	作用
Paths				|用于创建 Path 实例的工厂类
Path				|替代 File,表示文件系统路径:接口
Files				|提供丰富的静态文件操作方法
FileSystem			|表示文件系统
FileSystems			|文件系统的工厂类
FileStore			|表示文件存储(如磁盘分区)
WatchService		|监控文件系统变化
StandardOpenOption	|文件打开选项枚举
StandardCopyOption	|文件复制选项枚举
LinkOption			|链接处理选项枚举


#详细解析

1. Path 接口 - 核心路径表示
Path 接口替代了 File 类,提供了更强大的路径操作功能。

import java.nio.file.Path;
import java.nio.file.Paths;

public class PathExample {
    public static void main(String[] args) {
        // 创建 Path 对象
        Path path1 = Paths.get("/home/user/documents/file.txt");
        Path path2 = Paths.get("C:", "Users", "John", "file.txt");
        Path path3 = Paths.get("relative/path/to/file.txt");
        
        // 常用方法
        System.out.println("文件名: " + path1.getFileName());
        System.out.println("父目录: " + path1.getParent());
        System.out.println("根目录: " + path1.getRoot());
        System.out.println("名称元素数量: " + path1.getNameCount());
        
        // 路径解析和组合
        Path base = Paths.get("/base/directory");
        Path resolved = base.resolve("subdir/file.txt");
        System.out.println("解析后路径: " + resolved);
        
        // 相对化路径
        Path pathA = Paths.get("/a/b/c");
        Path pathB = Paths.get("/a/b/d/e");
        Path relative = pathA.relativize(pathB);
        System.out.println("相对路径: " + relative); // 输出: ../d/e
        
        // 规范化路径(处理 . 和 ..)
        Path complex = Paths.get("/a/./b/../c/d");
        Path normalized = complex.normalize();
        System.out.println("规范化后: " + normalized); // 输出: /a/c/d
    }
}

[root@host-11java_project]# javac -d . PathExample.java 
[root@host-11java_project]# java PathExample
文件名: file.txt
父目录: /home/user/documents
根目录: /
名称元素数量: 4
解析后路径: /base/directory/subdir/file.txt
相对路径: ../d/e
规范化后: /a/c/d

2. Files 类 - 强大的文件操作工具
Files 类提供了丰富的静态方法,是 NIO.2 的核心。

#文件检查和属性获取
import java.nio.file.*;
import java.io.IOException;
import java.util.Set;

public class FilesBasicOperations {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("example.txt");
        
        // 文件存在性检查
        System.out.println("文件存在: " + Files.exists(path));
        System.out.println("是常规文件: " + Files.isRegularFile(path));
        System.out.println("是目录: " + Files.isDirectory(path));
        System.out.println("可读: " + Files.isReadable(path));
        System.out.println("可写: " + Files.isWritable(path));
        System.out.println("可执行: " + Files.isExecutable(path));
        System.out.println("是隐藏文件: " + Files.isHidden(path));
        
        // 文件属性
        System.out.println("文件大小: " + Files.size(path) + " bytes");
        System.out.println("最后修改时间: " + Files.getLastModifiedTime(path));
        System.out.println("所有者: " + Files.getOwner(path));
        
        // 文件类型检测
        System.out.println("MIME类型: " + Files.probeContentType(path));
        
        // 获取文件存储信息
        FileStore store = Files.getFileStore(path);
        System.out.println("总空间: " + store.getTotalSpace());
        System.out.println("可用空间: " + store.getUsableSpace());
    }
}

#输出
[root@host-11java_project]# javac -d . FilesBasicOperations.java 
[root@host-11java_project]# java FilesBasicOperations
文件存在: true
是常规文件: true
是目录: false
可读: true
可写: true
可执行: false
是隐藏文件: false
文件大小: 164 bytes
最后修改时间: 2025-09-26T08:27:39Z
所有者: root
MIME类型: text/plain
总空间: 106288885760
可用空间: 32214536192

########################################################
#文件创建、复制、移动和删除

import java.nio.file.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

public class FileOperations {
    public static void main(String[] args) throws IOException {
        // 创建目录(支持多级创建)
        Path dir = Paths.get("my_directory/subdir");
        Files.createDirectories(dir);
        
        // 创建文件
        Path file = Paths.get("example.txt");
        Files.createFile(file);
        
        // 写入文件
        List<String> lines = Arrays.asList("第一行", "第二行", "第三行");
        Files.write(file, lines, StandardCharsets.UTF_8);
        
        // 追加内容
        Files.write(file, Arrays.asList("追加行"), 
                   StandardCharsets.UTF_8, StandardOpenOption.APPEND);
        
        // 读取文件
        List<String> content = Files.readAllLines(file, StandardCharsets.UTF_8);
        content.forEach(System.out::println);
        
        // 复制文件
        Path copy = Paths.get("example_copy.txt");
        Files.copy(file, copy, StandardCopyOption.REPLACE_EXISTING);
        
        // 移动/重命名文件
        Path moved = Paths.get("renamed_example.txt");
        Files.move(file, moved, StandardCopyOption.REPLACE_EXISTING);
        
        // 删除文件
        Files.deleteIfExists(copy);
    }
}

[root@host-11java_project]# java FileOperations
第一行
第二行
第三行
追加行

##########################################################################
#文件和目录遍历
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;

public class FileTraversal {
    public static void main(String[] args) throws IOException {
        Path startDir = Paths.get(".");
        
        // 使用 Files.walk 递归遍历(深度优先)
        System.out.println("=== 递归遍历所有文件 ===");
        try (Stream<Path> stream = Files.walk(startDir)) {
            stream.forEach(path -> {
                try {
                    String type = Files.isDirectory(path) ? "目录" : "文件";    //如果是目录返回"目录",否则范围"文件"
                    String size = Files.isDirectory(path) ? "" : " (" + Files.size(path) + " bytes)";
                    System.out.println(type + ": " + path + size);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 使用 Files.list 非递归遍历(仅当前目录)
        System.out.println("\n=== 当前目录内容 ===");
        try (Stream<Path> stream = Files.list(startDir)) {
            stream.forEach(System.out::println);
        }
        
        // 使用 Files.find 带条件查找
        System.out.println("\n=== 查找 .java 文件 ===");
        try (Stream<Path> stream = Files.find(startDir, Integer.MAX_VALUE,(path, attr) -> path.toString().endsWith(".java"))) {
            stream.forEach(System.out::println);
        }
    }
}

#
[root@host-11java_project]# javac -d . FileTraversal.java 
[root@host-11java_project]# java FileTraversal
=== 递归遍历所有文件 ===
目录: .
文件: ./FileTraversal.class (3849 bytes)
文件: ./PrintDir.java (242 bytes)
文件: ./PrintDir.class (600 bytes)
文件: ./FileMethodsDemo.java (2530 bytes)
文件: ./FileMethodsDemo.class (2723 bytes)
文件: ./PathExample.java (1344 bytes)
文件: ./PathExample.class (1788 bytes)
文件: ./FilesBasicOperations.java (1366 bytes)
文件: ./FilesBasicOperations.class (2416 bytes)
文件: ./FileOperations.java (1378 bytes)
文件: ./FileOperations.class (2672 bytes)
目录: ./my_directory
目录: ./my_directory/subdir
文件: ./renamed_example.txt (40 bytes)
文件: ./FileTraversal.java (1466 bytes)

=== 当前目录内容 ===
./FileTraversal.class
./PrintDir.java
./PrintDir.class
./FileMethodsDemo.java
./FileMethodsDemo.class
./PathExample.java
./PathExample.class
./FilesBasicOperations.java
./FilesBasicOperations.class
./FileOperations.java
./FileOperations.class
./my_directory
./renamed_example.txt
./FileTraversal.java

=== 查找 .java 文件 ===
./PrintDir.java
./FileMethodsDemo.java
./PathExample.java
./FilesBasicOperations.java
./FileOperations.java
./FileTraversal.java

###############################################################################
3. 文件属性和权限管理
#NIO.2 提供了更精细的文件属性控制。

import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;
import java.util.Set;

public class FileAttributes {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test_file.txt");
        Files.createFile(path);
        
        // 基本文件属性
        BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
        System.out.println("创建时间: " + attrs.creationTime());
        System.out.println("最后访问时间: " + attrs.lastAccessTime());
        System.out.println("最后修改时间: " + attrs.lastModifiedTime());
        System.out.println("是符号链接: " + attrs.isSymbolicLink());
        
        // 设置文件权限(POSIX系统)
        if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
            Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
            Files.setPosixFilePermissions(path, perms);
            
            Set<PosixFilePermission> currentPerms = Files.getPosixFilePermissions(path);
            System.out.println("当前权限: " + PosixFilePermissions.toString(currentPerms));
        }
        
        // 设置文件所有者
        UserPrincipal owner = path.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByName(System.getProperty("user.name"));
        Files.setOwner(path, owner);
        
        // 使用 FileAttribute 在创建时设置属性
        Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r--r--");
        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
        
        Path newFile = Paths.get("new_file.txt");
        Files.createFile(newFile, attr);
        
        // 清理
        Files.deleteIfExists(path);
        Files.deleteIfExists(newFile);
    }
}

###
[root@host-11 java_project]# javac -d . FileAttributes.java
[root@host-11 java_project]# java FileAttributes
创建时间: 2025-09-26T09:01:24Z
最后访问时间: 2025-09-26T09:01:24Z
最后修改时间: 2025-09-26T09:01:24Z
是符号链接: false
当前权限: rwxr-x---

4. 文件系统监控 (WatchService)
WatchService 可以监控目录的变化,非常适合需要实时响应文件变化的场景。

#java
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class DirectoryWatcher {
    public static void main(String[] args) throws IOException, InterruptedException {
        Path watchDir = Paths.get(".").toAbsolutePath().normalize();
        System.out.println("监控目录: " + watchDir);
        
        // 创建 WatchService
        WatchService watchService = FileSystems.getDefault().newWatchService();
        
        // 注册要监控的事件类型
        watchDir.register(watchService, 
            ENTRY_CREATE, 
            ENTRY_DELETE, 
            ENTRY_MODIFY);
        
        System.out.println("开始监控目录变化 (Ctrl+C 退出)...");
        
        while (true) {
            WatchKey key;
            try {
                // 等待事件发生,最多等待1分钟
                key = watchService.poll(1, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                break;
            }
            
            if (key == null) {
                System.out.println("监控超时,继续等待...");
                continue;
            }
            
            // 处理所有事件
            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();
                
                // 处理溢出事件(可能丢失了一些事件)
                if (kind == OVERFLOW) {
                    System.out.println("事件可能丢失");
                    continue;
                }
                
                // 获取触发事件的文件名
                @SuppressWarnings("unchecked")
                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                Path fileName = ev.context();
                
                System.out.printf("事件类型: %s, 文件: %s%n", kind.name(), fileName);
                
                // 根据事件类型执行不同操作
                if (kind == ENTRY_CREATE) {
                    System.out.println("新文件创建: " + fileName);
                } else if (kind == ENTRY_DELETE) {
                    System.out.println("文件删除: " + fileName);
                } else if (kind == ENTRY_MODIFY) {
                    System.out.println("文件修改: " + fileName);
                }
            }
            
            // 重置key,继续接收事件
            boolean valid = key.reset();
            if (!valid) {
                System.out.println("监控Key无效,退出监控");
                break;
            }
        }
        
        watchService.close();
    }
}

#####
[root@host-11 java_project]# javac -d  . DirectoryWatcher.java 
[root@host-11 java_project]# java DirectoryWatcher
监控目录: /root/java_project
开始监控目录变化 (Ctrl+C 退出)...
事件类型: ENTRY_CREATE, 文件: mysqljava.txt
新文件创建: mysqljava.txt
事件类型: ENTRY_DELETE, 文件: mysqljava.txt
文件删除: mysqljava.txt
事件类型: ENTRY_CREATE, 文件: 1.txt
新文件创建: 1.txt
事件类型: ENTRY_MODIFY, 文件: 1.txt
文件修改: 1.txt
事件类型: ENTRY_CREATE, 文件: .1.txt.swp
新文件创建: .1.txt.swp
事件类型: ENTRY_CREATE, 文件: .1.txt.swx
新文件创建: .1.txt.swx
事件类型: ENTRY_DELETE, 文件: .1.txt.swx
文件删除: .1.txt.swx
事件类型: ENTRY_DELETE, 文件: .1.txt.swp
文件删除: .1.txt.swp
事件类型: ENTRY_CREATE, 文件: .1.txt.swp
新文件创建: .1.txt.swp
事件类型: ENTRY_MODIFY, 文件: .1.txt.swp
文件修改: .1.txt.swp
事件类型: ENTRY_MODIFY, 文件: .1.txt.swp
文件修改: .1.txt.swp
事件类型: ENTRY_CREATE, 文件: 4913
新文件创建: 4913
事件类型: ENTRY_MODIFY, 文件: 4913
文件修改: 4913
事件类型: ENTRY_DELETE, 文件: 4913
文件删除: 4913
事件类型: ENTRY_DELETE, 文件: 1.txt
文件删除: 1.txt
事件类型: ENTRY_CREATE, 文件: 1.txt~
新文件创建: 1.txt~
事件类型: ENTRY_CREATE, 文件: 1.txt
新文件创建: 1.txt
事件类型: ENTRY_MODIFY, 文件: 1.txt
文件修改: 1.txt
事件类型: ENTRY_MODIFY, 文件: 1.txt
文件修改: 1.txt
事件类型: ENTRY_MODIFY, 文件: .1.txt.swp
文件修改: .1.txt.swp
事件类型: ENTRY_DELETE, 文件: 1.txt~
文件删除: 1.txt~
事件类型: ENTRY_DELETE, 文件: .1.txt.swp
文件删除: .1.txt.swp
事件类型: ENTRY_MODIFY, 文件: 1.txt
文件修改: 1.txt

5. 符号链接和硬链接
#java
import java.nio.file.*;
import java.io.IOException;

public class LinkOperations {
    public static void main(String[] args) throws IOException {
        Path original = Paths.get("original.txt");
        Files.write(original, "原始文件内容".getBytes());
        
        // 创建符号链接(需要管理员权限或在支持的系统上)
        Path symLink = Paths.get("symbolic_link.txt");
        try {
            Files.createSymbolicLink(symLink, original);
            System.out.println("符号链接创建成功");
            
            // 检查是否是符号链接
            System.out.println("是符号链接: " + Files.isSymbolicLink(symLink));
            
            // 读取符号链接目标
            Path target = Files.readSymbolicLink(symLink);
            System.out.println("符号链接指向: " + target);
        } catch (UnsupportedOperationException | IOException e) {
            System.out.println("符号链接创建失败: " + e.getMessage());
        }
        
        // 创建硬链接
        Path hardLink = Paths.get("hard_link.txt");
        try {
            Files.createLink(hardLink, original);
            System.out.println("硬链接创建成功");
        } catch (UnsupportedOperationException | IOException e) {
            System.out.println("硬链接创建失败: " + e.getMessage());
        }
        
        // 清理
        Files.deleteIfExists(original);
        Files.deleteIfExists(symLink);
        Files.deleteIfExists(hardLink);
    }
}

#NIO.2 相比传统 I/O 的优势
更好的性能:使用更高效的缓冲区管理和非阻塞 I/O
更丰富的功能:符号链接、文件属性视图、文件系统监控等
更一致的 API:统一的异常处理,更好的方法命名
更强的扩展性:支持自定义文件系统提供程序
更好的错误处理:更详细的异常信息

#最佳实践
优先使用 NIO.2:在新项目中使用 java.nio.file 而不是 java.io.File
使用 try-with-resources:确保资源正确关闭
处理异常:NIO.2 方法可能抛出多种 IOException
考虑性能:对于大文件操作,使用适当的缓冲策略
注意平台差异:某些功能(如符号链接)在不同操作系统上表现不同
NIO.2 提供了现代化、功能丰富的文件操作 API,是 Java 文件处理的推荐方式。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐