← 返回首页
📂

Java NIO.2文件系统API详解

📂 java ⏱ 4 min 748 words

Java NIO.2文件系统API详解

概述

Java NIO.2(Java 7引入)提供了更强大、更易用的文件系统API。新的API简化了文件操作,支持异步IO和文件属性访问。

1. Path类

创建和操作Path

import java.nio.file.*;

public class PathExample {
    public static void main(String[] args) {
        // 创建Path
        Path path1 = Paths.get("/home/user/file.txt");
        Path path2 = Path.of("home", "user", "file.txt");
        Path path3 = Path.of("C:\\Users\\user\\file.txt");
        
        System.out.println("Path: " + path1);
        System.out.println("文件名: " + path1.getFileName());
        System.out.println("父路径: " + path1.getParent());
        System.out.println("根路径: " + path1.getRoot());
        System.out.println("路径元素数: " + path1.getNameCount());
        
        // 路径操作
        Path base = Paths.get("/home/user");
        Path relative = base.resolve("documents/file.txt");
        System.out.println("resolve: " + relative);
        
        Path absolute = Paths.get("/tmp").resolve(path1);
        System.out.println("绝对路径: " + absolute);
        
        // 规范化路径
        Path messy = Paths.get("/home/user/../user/./file.txt");
        System.out.println("规范化: " + messy.normalize());
        
        // 相对路径
        Path pathA = Paths.get("/home/user/a");
        Path pathB = Paths.get("/home/user/b");
        Path relativePath = pathA.relativize(pathB);
        System.out.println("相对路径: " + relativePath);
        
        // 路径比较
        System.out.println("路径相等: " + path1.equals(path2));
    }
}

2. Files工具类

文件操作

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class FilesBasicExample {
    public static void main(String[] args) {
        try {
            // 创建文件
            Path newFile = Path.of("test-nio2.txt");
            Files.createFile(newFile);
            System.out.println("文件已创建: " + newFile);
            
            // 写入文件
            Files.writeString(newFile, "Hello, NIO.2!", StandardCharsets.UTF_8);
            System.out.println("文件已写入");
            
            // 读取文件
            String content = Files.readString(newFile, StandardCharsets.UTF_8);
            System.out.println("文件内容: " + content);
            
            // 读取所有行
            List<String> lines = Files.readAllLines(newFile, StandardCharsets.UTF_8);
            System.out.println("所有行: " + lines);
            
            // 读取所有字节
            byte[] bytes = Files.readAllBytes(newFile);
            System.out.println("字节数: " + bytes.length);
            
            // 追加内容
            Files.writeString(newFile, "\n第二行内容", 
                StandardCharsets.UTF_8, 
                StandardOpenOption.APPEND);
            
            // 复制文件
            Path copyPath = Path.of("test-nio2-copy.txt");
            Files.copy(newFile, copyPath, StandardCopyOption.REPLACE_EXISTING);
            System.out.println("文件已复制: " + copyPath);
            
            // 移动文件
            Path movePath = Path.of("test-nio2-moved.txt");
            Files.move(copyPath, movePath, StandardCopyOption.REPLACE_EXISTING);
            System.out.println("文件已移动: " + movePath);
            
            // 删除文件
            Files.delete(newFile);
            Files.delete(movePath);
            System.out.println("文件已删除");
            
            // 删除不存在的文件(不抛异常)
            boolean deleted = Files.deleteIfExists(Path.of("nonexistent.txt"));
            System.out.println("deleteIfExists: " + deleted);
            
        } catch (Exception e) {
            System.out.println("异常: " + e.getMessage());
        }
    }
}

目录操作

import java.nio.file.*;
import java.util.*;
import java.util.stream.*;

public class DirectoryExample {
    public static void main(String[] args) {
        try {
            // 创建目录
            Path dir = Path.of("test-dir");
            Files.createDirectory(dir);
            System.out.println("目录已创建: " + dir);
            
            // 创建多级目录
            Path multiDir = Path.of("test-dir/sub1/sub2");
            Files.createDirectories(multiDir);
            System.out.println("多级目录已创建: " + multiDir);
            
            // 创建临时文件
            Path tempFile = Files.createTempFile(dir, "temp", ".txt");
            System.out.println("临时文件: " + tempFile);
            
            // 创建临时目录
            Path tempDir = Files.createTempDirectory(dir, "tempdir");
            System.out.println("临时目录: " + tempDir);
            
            // 遍历目录
            System.out.println("目录内容:");
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
                for (Path entry : stream) {
                    System.out.println("  " + entry.getFileName());
                }
            }
            
            // 使用Files.list
            System.out.println("Files.list:");
            try (Stream<Path> paths = Files.list(dir)) {
                paths.forEach(p -> System.out.println("  " + p.getFileName()));
            }
            
            // 使用Files.walk
            System.out.println("Files.walk:");
            try (Stream<Path> paths = Files.walk(dir)) {
                paths.forEach(p -> System.out.println("  " + p));
            }
            
            // 使用Files.find
            System.out.println("Files.find (txt文件):");
            try (Stream<Path> paths = Files.find(dir, 10, 
                    (path, attrs) -> path.toString().endsWith(".txt"))) {
                paths.forEach(p -> System.out.println("  " + p));
            }
            
            // 清理
            Files.deleteIfExists(tempFile);
            Files.deleteIfExists(tempDir);
            Files.deleteIfExists(dir);
            
        } catch (Exception e) {
            System.out.println("异常: " + e.getMessage());
        }
    }
}

3. 文件属性

import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;

public class FileAttributesExample {
    public static void main(String[] args) {
        try {
            Path file = Path.of("test-attributes.txt");
            Files.writeString(file, "Test content");
            
            // 基本属性
            System.out.println("文件存在: " + Files.exists(file));
            System.out.println("是文件: " + Files.isRegularFile(file));
            System.out.println("是目录: " + Files.isDirectory(file));
            System.out.println("可读: " + Files.isReadable(file));
            System.out.println("可写: " + Files.isWritable(file));
            System.out.println("可执行: " + Files.isExecutable(file));
            
            // 文件大小
            long size = Files.size(file);
            System.out.println("文件大小: " + size + " 字节");
            
            // 最后修改时间
            java.nio.file.attribute.FileTime lastModified = Files.getLastModifiedTime(file);
            System.out.println("最后修改: " + lastModified);
            
            // 读取所有属性
            Map<String, Object> attrs = Files.readAttributes(file, "*", 
                LinkOption.NOFOLLOW_LINKS);
            System.out.println("所有属性: " + attrs);
            
            // 读取特定属性
            BasicFileAttributes basicAttrs = Files.readAttributes(file, 
                BasicFileAttributes.class);
            System.out.println("创建时间: " + basicAttrs.creationTime());
            System.out.println("最后访问: " + basicAttrs.lastAccessTime());
            System.out.println("最后修改: " + basicAttrs.lastModifiedTime());
            System.out.println("是符号链接: " + basicAttrs.isSymbolicLink());
            
            // 设置属性
            Files.setAttribute(file, "basic:lastModifiedTime", 
                java.nio.file.attribute.FileTime.fromMillis(System.currentTimeMillis()));
            
            // 修改权限
            Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
            perms.add(PosixFilePermission.OWNER_EXECUTE);
            Files.setPosixFilePermissions(file, perms);
            System.out.println("权限: " + perms);
            
            // 清理
            Files.delete(file);
            
        } catch (Exception e) {
            System.out.println("异常: " + e.getMessage());
        }
    }
}

4. 实际应用示例

文件搜索工具

import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.*;

public class FileSearchTool {
    public static List<Path> searchFiles(Path dir, String pattern) throws Exception {
        List<Path> results = new ArrayList<>();
        
        try (Stream<Path> paths = Files.walk(dir)) {
            results = paths
                .filter(Files::isRegularFile)
                .filter(path -> path.getFileName().toString().matches(pattern))
                .collect(Collectors.toList());
        }
        
        return results;
    }
    
    public static void main(String[] args) {
        try {
            List<Path> javaFiles = searchFiles(Path.of("."), ".*\\.java");
            System.out.println("找到的Java文件:");
            javaFiles.forEach(p -> System.out.println("  " + p));
            
        } catch (Exception e) {
            System.out.println("搜索异常: " + e.getMessage());
        }
    }
}

文件备份工具

import java.nio.file.*;
import java.nio.file.attribute.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class BackupTool {
    public static void backupDirectory(Path source, Path dest) throws Exception {
        // 创建带时间戳的备份目录
        String timestamp = LocalDateTime.now().format(
            DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        Path backupDir = dest.resolve("backup_" + timestamp);
        Files.createDirectories(backupDir);
        
        // 复制所有文件
        try (Stream<Path> paths = Files.walk(source)) {
            paths.filter(Files::isRegularFile)
                 .forEach(sourceFile -> {
                     try {
                         Path relativePath = source.relativize(sourceFile);
                         Path destFile = backupDir.resolve(relativePath);
                         Files.createDirectories(destFile.getParent());
                         Files.copy(sourceFile, destFile, 
                             StandardCopyOption.REPLACE_EXISTING);
                         System.out.println("已备份: " + relativePath);
                     } catch (Exception e) {
                         System.out.println("备份失败: " + e.getMessage());
                     }
                 });
        }
        
        System.out.println("备份完成: " + backupDir);
    }
    
    public static void main(String[] args) {
        try {
            backupDirectory(Path.of("src"), Path.of("backups"));
        } catch (Exception e) {
            System.out.println("备份异常: " + e.getMessage());
        }
    }
}

5. 最佳实践

  1. 使用NIO.2 API:优先使用新的文件系统API
  2. 使用try-with-resources:自动关闭DirectoryStream等资源
  3. 使用Files工具类:简化文件操作
  4. 处理符号链接:注意LinkOption的使用
  5. 使用Path而不是File:Path更现代、更强大

总结

Java NIO.2提供了现代、强大的文件系统API。掌握Path、Files和文件属性的使用,可以高效地进行文件操作。在实际编程中,优先使用NIO.2 API而不是旧的File类。