14.1 I/O基础概念

14.1.1 I/O概述

I/O(Input/Output)是计算机程序与外部世界进行数据交换的重要机制。Java提供了丰富的I/O API来处理各种数据源和目标,包括文件、网络连接、内存缓冲区等。

I/O的分类: - 按数据流向:输入流(Input)、输出流(Output) - 按数据类型:字节流(Byte Stream)、字符流(Character Stream) - 按功能特点:节点流(Node Stream)、处理流(Processing Stream)

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

// I/O基础概念演示
public class IOBasicsDemo {
    
    public static void main(String[] args) {
        // I/O流的层次结构演示
        demonstrateStreamHierarchy();
        
        // 文件系统基础操作
        demonstrateFileSystemBasics();
        
        // I/O异常处理
        demonstrateIOExceptionHandling();
    }
    
    // I/O流的层次结构演示
    public static void demonstrateStreamHierarchy() {
        System.out.println("=== I/O流的层次结构 ===");
        
        // 字节流基类
        System.out.println("字节流基类:");
        System.out.println("  InputStream - 字节输入流的抽象基类");
        System.out.println("  OutputStream - 字节输出流的抽象基类");
        
        // 字符流基类
        System.out.println("\n字符流基类:");
        System.out.println("  Reader - 字符输入流的抽象基类");
        System.out.println("  Writer - 字符输出流的抽象基类");
        
        // 常用实现类
        System.out.println("\n常用字节流实现类:");
        System.out.println("  FileInputStream/FileOutputStream - 文件字节流");
        System.out.println("  ByteArrayInputStream/ByteArrayOutputStream - 字节数组流");
        System.out.println("  BufferedInputStream/BufferedOutputStream - 缓冲字节流");
        
        System.out.println("\n常用字符流实现类:");
        System.out.println("  FileReader/FileWriter - 文件字符流");
        System.out.println("  StringReader/StringWriter - 字符串流");
        System.out.println("  BufferedReader/BufferedWriter - 缓冲字符流");
    }
    
    // 文件系统基础操作
    public static void demonstrateFileSystemBasics() {
        System.out.println("\n=== 文件系统基础操作 ===");
        
        // File类基础操作
        File file = new File("test.txt");
        System.out.println("文件名: " + file.getName());
        System.out.println("绝对路径: " + file.getAbsolutePath());
        System.out.println("父目录: " + file.getParent());
        System.out.println("是否存在: " + file.exists());
        System.out.println("是否为文件: " + file.isFile());
        System.out.println("是否为目录: " + file.isDirectory());
        
        // 目录操作
        File directory = new File("testDir");
        if (!directory.exists()) {
            boolean created = directory.mkdir();
            System.out.println("创建目录: " + created);
        }
        
        // 列出目录内容
        File currentDir = new File(".");
        String[] files = currentDir.list();
        if (files != null) {
            System.out.println("\n当前目录内容:");
            for (String fileName : files) {
                System.out.println("  " + fileName);
            }
        }
        
        // 文件属性
        if (file.exists()) {
            System.out.println("\n文件属性:");
            System.out.println("  大小: " + file.length() + " 字节");
            System.out.println("  最后修改时间: " + new Date(file.lastModified()));
            System.out.println("  可读: " + file.canRead());
            System.out.println("  可写: " + file.canWrite());
            System.out.println("  可执行: " + file.canExecute());
        }
    }
    
    // I/O异常处理
    public static void demonstrateIOExceptionHandling() {
        System.out.println("\n=== I/O异常处理 ===");
        
        // 传统的try-catch-finally
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("nonexistent.txt");
            // 读取操作...
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("I/O异常: " + e.getMessage());
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    System.out.println("关闭流时发生异常: " + e.getMessage());
                }
            }
        }
        
        // try-with-resources(推荐方式)
        try (FileInputStream fis2 = new FileInputStream("nonexistent.txt")) {
            // 读取操作...
        } catch (FileNotFoundException e) {
            System.out.println("使用try-with-resources处理文件未找到: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("使用try-with-resources处理I/O异常: " + e.getMessage());
        }
        
        // 多个资源的try-with-resources
        try (FileInputStream input = new FileInputStream("input.txt");
             FileOutputStream output = new FileOutputStream("output.txt")) {
            // 复制操作...
        } catch (IOException e) {
            System.out.println("多资源操作异常: " + e.getMessage());
        }
    }
}

14.5 对象序列化

14.5.1 基础序列化

import java.io.*;
import java.util.*;

// 序列化演示类
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private transient String password; // transient字段不会被序列化
    private static String country = "China"; // static字段不会被序列化
    
    public Person(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }
    
    // getter和setter方法
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public static String getCountry() { return country; }
    public static void setCountry(String country) { Person.country = country; }
    
    @Override
    public String toString() {
        return String.format("Person{name='%s', age=%d, password='%s', country='%s'}", 
            name, age, password, country);
    }
}

// 自定义序列化的类
class Student implements Serializable {
    private static final long serialVersionUID = 2L;
    
    private String name;
    private int score;
    private Date enrollDate;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
        this.enrollDate = new Date();
    }
    
    // 自定义序列化方法
    private void writeObject(ObjectOutputStream out) throws IOException {
        System.out.println("执行自定义序列化: " + name);
        out.defaultWriteObject(); // 执行默认序列化
        out.writeUTF("额外的序列化数据"); // 写入额外数据
    }
    
    // 自定义反序列化方法
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        System.out.println("执行自定义反序列化");
        in.defaultReadObject(); // 执行默认反序列化
        String extraData = in.readUTF(); // 读取额外数据
        System.out.println("读取到额外数据: " + extraData);
    }
    
    // getter方法
    public String getName() { return name; }
    public int getScore() { return score; }
    public Date getEnrollDate() { return enrollDate; }
    
    @Override
    public String toString() {
        return String.format("Student{name='%s', score=%d, enrollDate=%s}", 
            name, score, enrollDate);
    }
}

// 对象序列化演示
public class SerializationDemo {
    
    public static void main(String[] args) {
        // 基础序列化演示
        demonstrateBasicSerialization();
        
        // 集合序列化演示
        demonstrateCollectionSerialization();
        
        // 自定义序列化演示
        demonstrateCustomSerialization();
        
        // 序列化版本控制
        demonstrateVersionControl();
    }
    
    // 基础序列化演示
    public static void demonstrateBasicSerialization() {
        System.out.println("=== 基础序列化演示 ===");
        
        String fileName = "person.ser";
        
        // 创建对象
        Person person = new Person("张三", 25, "secret123");
        System.out.println("原始对象: " + person);
        
        // 序列化对象
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(fileName))) {
            
            oos.writeObject(person);
            System.out.println("对象序列化完成");
            
        } catch (IOException e) {
            System.out.println("序列化失败: " + e.getMessage());
            return;
        }
        
        // 反序列化对象
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(fileName))) {
            
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println("反序列化对象: " + deserializedPerson);
            
            // 验证transient和static字段
            System.out.println("\n字段验证:");
            System.out.println("  name: " + deserializedPerson.getName());
            System.out.println("  age: " + deserializedPerson.getAge());
            System.out.println("  password (transient): " + deserializedPerson.getPassword());
            System.out.println("  country (static): " + Person.getCountry());
            
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("反序列化失败: " + e.getMessage());
        }
        
        // 检查文件大小
        File serFile = new File(fileName);
        if (serFile.exists()) {
            System.out.println("\n序列化文件大小: " + serFile.length() + " 字节");
        }
    }
    
    // 集合序列化演示
    public static void demonstrateCollectionSerialization() {
        System.out.println("\n=== 集合序列化演示 ===");
        
        String fileName = "collection.ser";
        
        // 创建集合
        List<Person> personList = Arrays.asList(
            new Person("Alice", 30, "pass1"),
            new Person("Bob", 25, "pass2"),
            new Person("Charlie", 35, "pass3")
        );
        
        Map<String, Integer> scoreMap = new HashMap<>();
        scoreMap.put("Math", 95);
        scoreMap.put("English", 87);
        scoreMap.put("Science", 92);
        
        System.out.println("原始集合:");
        System.out.println("  人员列表: " + personList.size() + " 个人员");
        System.out.println("  成绩映射: " + scoreMap);
        
        // 序列化集合
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(fileName))) {
            
            oos.writeObject(personList);
            oos.writeObject(scoreMap);
            System.out.println("\n集合序列化完成");
            
        } catch (IOException e) {
            System.out.println("集合序列化失败: " + e.getMessage());
            return;
        }
        
        // 反序列化集合
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(fileName))) {
            
            @SuppressWarnings("unchecked")
            List<Person> deserializedList = (List<Person>) ois.readObject();
            
            @SuppressWarnings("unchecked")
            Map<String, Integer> deserializedMap = (Map<String, Integer>) ois.readObject();
            
            System.out.println("\n反序列化集合:");
            System.out.println("  人员列表:");
            deserializedList.forEach(person -> System.out.println("    " + person));
            
            System.out.println("  成绩映射: " + deserializedMap);
            
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("集合反序列化失败: " + e.getMessage());
        }
    }
    
    // 自定义序列化演示
    public static void demonstrateCustomSerialization() {
        System.out.println("\n=== 自定义序列化演示 ===");
        
        String fileName = "student.ser";
        
        // 创建学生对象
        Student student = new Student("李四", 88);
        System.out.println("原始学生对象: " + student);
        
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(fileName))) {
            
            oos.writeObject(student);
            System.out.println("\n学生对象序列化完成");
            
        } catch (IOException e) {
            System.out.println("学生对象序列化失败: " + e.getMessage());
            return;
        }
        
        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(fileName))) {
            
            Student deserializedStudent = (Student) ois.readObject();
            System.out.println("\n反序列化学生对象: " + deserializedStudent);
            
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("学生对象反序列化失败: " + e.getMessage());
        }
    }
    
    // 序列化版本控制
    public static void demonstrateVersionControl() {
        System.out.println("\n=== 序列化版本控制 ===");
        
        System.out.println("Person类的serialVersionUID: " + getSerialVersionUID(Person.class));
        System.out.println("Student类的serialVersionUID: " + getSerialVersionUID(Student.class));
        
        // 演示版本兼容性
        System.out.println("\n版本兼容性说明:");
        System.out.println("  - serialVersionUID相同: 兼容");
        System.out.println("  - serialVersionUID不同: 抛出InvalidClassException");
        System.out.println("  - 未定义serialVersionUID: 由JVM自动生成,类结构变化时会改变");
    }
    
    private static long getSerialVersionUID(Class<?> clazz) {
        try {
            return clazz.getDeclaredField("serialVersionUID").getLong(null);
        } catch (Exception e) {
            return -1; // 未定义或获取失败
        }
    }
}

14.6 NIO编程基础

14.6.1 Channel和Buffer

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.*;

// NIO编程演示
public class NIODemo {
    
    public static void main(String[] args) {
        // Buffer操作演示
        demonstrateBuffer();
        
        // Channel操作演示
        demonstrateChannel();
        
        // 文件Channel操作
        demonstrateFileChannel();
        
        // 内存映射文件
        demonstrateMemoryMappedFile();
    }
    
    // Buffer操作演示
    public static void demonstrateBuffer() {
        System.out.println("=== Buffer操作演示 ===");
        
        // ByteBuffer操作
        ByteBuffer buffer = ByteBuffer.allocate(10);
        System.out.println("初始状态: " + bufferInfo(buffer));
        
        // 写入数据
        buffer.put((byte) 'H');
        buffer.put((byte) 'e');
        buffer.put((byte) 'l');
        buffer.put((byte) 'l');
        buffer.put((byte) 'o');
        System.out.println("写入Hello后: " + bufferInfo(buffer));
        
        // 切换到读模式
        buffer.flip();
        System.out.println("flip后: " + bufferInfo(buffer));
        
        // 读取数据
        System.out.println("\n读取数据:");
        while (buffer.hasRemaining()) {
            byte b = buffer.get();
            System.out.print((char) b);
        }
        System.out.println();
        System.out.println("读取完成后: " + bufferInfo(buffer));
        
        // 重置和清空
        buffer.rewind(); // 重置position到0
        System.out.println("rewind后: " + bufferInfo(buffer));
        
        buffer.clear(); // 清空buffer
        System.out.println("clear后: " + bufferInfo(buffer));
        
        // 直接Buffer vs 非直接Buffer
        demonstrateDirectBuffer();
    }
    
    private static String bufferInfo(Buffer buffer) {
        return String.format("position=%d, limit=%d, capacity=%d, remaining=%d", 
            buffer.position(), buffer.limit(), buffer.capacity(), buffer.remaining());
    }
    
    private static void demonstrateDirectBuffer() {
        System.out.println("\n直接Buffer vs 非直接Buffer:");
        
        // 非直接Buffer
        ByteBuffer heapBuffer = ByteBuffer.allocate(1024);
        System.out.println("堆Buffer - isDirect: " + heapBuffer.isDirect());
        
        // 直接Buffer
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
        System.out.println("直接Buffer - isDirect: " + directBuffer.isDirect());
        
        // 性能测试
        int iterations = 100000;
        byte[] data = "Hello NIO Buffer Performance Test".getBytes();
        
        // 堆Buffer性能
        long startTime = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            heapBuffer.clear();
            heapBuffer.put(data);
            heapBuffer.flip();
        }
        long heapTime = System.nanoTime() - startTime;
        
        // 直接Buffer性能
        startTime = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            directBuffer.clear();
            directBuffer.put(data);
            directBuffer.flip();
        }
        long directTime = System.nanoTime() - startTime;
        
        System.out.println("性能测试 (" + iterations + " 次操作):");
        System.out.println("  堆Buffer: " + heapTime / 1000000 + "ms");
        System.out.println("  直接Buffer: " + directTime / 1000000 + "ms");
    }
    
    // Channel操作演示
    public static void demonstrateChannel() {
        System.out.println("\n=== Channel操作演示 ===");
        
        String fileName = "nio_test.txt";
        String content = "这是NIO Channel测试内容\n包含多行文本\n用于演示Channel操作";
        
        // 使用Channel写入文件
        try (FileChannel writeChannel = FileChannel.open(
                Paths.get(fileName), 
                StandardOpenOption.CREATE, 
                StandardOpenOption.WRITE, 
                StandardOpenOption.TRUNCATE_EXISTING)) {
            
            ByteBuffer writeBuffer = ByteBuffer.wrap(content.getBytes("UTF-8"));
            int bytesWritten = writeChannel.write(writeBuffer);
            
            System.out.println("写入字节数: " + bytesWritten);
            System.out.println("Channel位置: " + writeChannel.position());
            System.out.println("Channel大小: " + writeChannel.size());
            
        } catch (IOException e) {
            System.out.println("Channel写入失败: " + e.getMessage());
            return;
        }
        
        // 使用Channel读取文件
        try (FileChannel readChannel = FileChannel.open(
                Paths.get(fileName), StandardOpenOption.READ)) {
            
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            int bytesRead = readChannel.read(readBuffer);
            
            readBuffer.flip();
            String readContent = new String(readBuffer.array(), 0, bytesRead, "UTF-8");
            
            System.out.println("\n读取内容:");
            System.out.println(readContent);
            System.out.println("读取字节数: " + bytesRead);
            
        } catch (IOException e) {
            System.out.println("Channel读取失败: " + e.getMessage());
        }
    }
    
    // 文件Channel操作
    public static void demonstrateFileChannel() {
        System.out.println("\n=== 文件Channel操作 ===");
        
        String sourceFile = "source_channel.txt";
        String targetFile = "target_channel.txt";
        
        // 创建源文件
        try {
            Files.write(Paths.get(sourceFile), 
                "这是用于Channel复制测试的源文件内容\n包含多行数据\n用于演示文件传输".getBytes("UTF-8"));
        } catch (IOException e) {
            System.out.println("创建源文件失败: " + e.getMessage());
            return;
        }
        
        // 使用transferTo方法复制文件
        try (FileChannel sourceChannel = FileChannel.open(
                Paths.get(sourceFile), StandardOpenOption.READ);
             FileChannel targetChannel = FileChannel.open(
                Paths.get(targetFile), 
                StandardOpenOption.CREATE, 
                StandardOpenOption.WRITE, 
                StandardOpenOption.TRUNCATE_EXISTING)) {
            
            long startTime = System.nanoTime();
            long bytesTransferred = sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
            long transferTime = System.nanoTime() - startTime;
            
            System.out.println("transferTo复制:");
            System.out.println("  传输字节数: " + bytesTransferred);
            System.out.println("  传输时间: " + transferTime / 1000000 + "ms");
            
        } catch (IOException e) {
            System.out.println("transferTo复制失败: " + e.getMessage());
        }
        
        // 验证复制结果
        try {
            String sourceContent = Files.readString(Paths.get(sourceFile));
            String targetContent = Files.readString(Paths.get(targetFile));
            
            boolean contentMatch = sourceContent.equals(targetContent);
            System.out.println("  内容匹配: " + contentMatch);
            
        } catch (IOException e) {
            System.out.println("验证复制结果失败: " + e.getMessage());
        }
        
        // Channel位置操作
        demonstrateChannelPosition(sourceFile);
    }
    
    private static void demonstrateChannelPosition(String fileName) {
        System.out.println("\nChannel位置操作:");
        
        try (FileChannel channel = FileChannel.open(
                Paths.get(fileName), StandardOpenOption.READ)) {
            
            System.out.println("  初始位置: " + channel.position());
            System.out.println("  文件大小: " + channel.size());
            
            // 移动到文件中间
            long middlePosition = channel.size() / 2;
            channel.position(middlePosition);
            System.out.println("  移动到中间位置: " + channel.position());
            
            // 从中间位置读取
            ByteBuffer buffer = ByteBuffer.allocate(20);
            int bytesRead = channel.read(buffer);
            
            buffer.flip();
            String content = new String(buffer.array(), 0, bytesRead, "UTF-8");
            System.out.println("  从中间读取内容: " + content.replace("\n", "\\n"));
            
        } catch (IOException e) {
            System.out.println("Channel位置操作失败: " + e.getMessage());
        }
    }
    
    // 内存映射文件
    public static void demonstrateMemoryMappedFile() {
        System.out.println("\n=== 内存映射文件 ===");
        
        String fileName = "memory_mapped.txt";
        String content = "这是内存映射文件测试内容,用于演示MappedByteBuffer的使用";
        
        try {
            // 创建测试文件
            Files.write(Paths.get(fileName), content.getBytes("UTF-8"));
            
            // 内存映射文件
            try (RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
                 FileChannel channel = raf.getChannel()) {
                
                // 映射整个文件到内存
                MappedByteBuffer mappedBuffer = channel.map(
                    FileChannel.MapMode.READ_WRITE, 0, channel.size());
                
                System.out.println("文件映射信息:");
                System.out.println("  映射大小: " + mappedBuffer.capacity());
                System.out.println("  是否为直接Buffer: " + mappedBuffer.isDirect());
                
                // 读取映射内容
                byte[] data = new byte[mappedBuffer.remaining()];
                mappedBuffer.get(data);
                String mappedContent = new String(data, "UTF-8");
                System.out.println("  映射内容: " + mappedContent);
                
                // 修改映射内容
                mappedBuffer.position(0);
                String newContent = "修改后的内容: " + System.currentTimeMillis();
                mappedBuffer.put(newContent.getBytes("UTF-8"));
                
                // 强制同步到磁盘
                mappedBuffer.force();
                System.out.println("  内容已修改并同步到磁盘");
                
            }
            
            // 验证修改结果
            String modifiedContent = Files.readString(Paths.get(fileName));
            System.out.println("  验证修改结果: " + modifiedContent.substring(0, Math.min(30, modifiedContent.length())));
            
        } catch (IOException e) {
            System.out.println("内存映射文件操作失败: " + e.getMessage());
        }
        
        // 性能对比:内存映射 vs 传统I/O
        performanceComparisonMappedVsTraditional();
    }
    
    private static void performanceComparisonMappedVsTraditional() {
        System.out.println("\n性能对比: 内存映射 vs 传统I/O");
        
        String fileName = "performance_test.dat";
        int fileSize = 1024 * 1024; // 1MB
        byte[] testData = new byte[fileSize];
        Arrays.fill(testData, (byte) 'A');
        
        try {
            // 创建测试文件
            Files.write(Paths.get(fileName), testData);
            
            // 传统I/O读取
            long startTime = System.nanoTime();
            try (FileInputStream fis = new FileInputStream(fileName)) {
                byte[] buffer = new byte[8192];
                int totalBytes = 0;
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    totalBytes += bytesRead;
                }
            }
            long traditionalTime = System.nanoTime() - startTime;
            
            // 内存映射读取
            startTime = System.nanoTime();
            try (RandomAccessFile raf = new RandomAccessFile(fileName, "r");
                 FileChannel channel = raf.getChannel()) {
                
                MappedByteBuffer mappedBuffer = channel.map(
                    FileChannel.MapMode.READ_ONLY, 0, channel.size());
                
                int totalBytes = 0;
                while (mappedBuffer.hasRemaining()) {
                    mappedBuffer.get();
                    totalBytes++;
                }
            }
            long mappedTime = System.nanoTime() - startTime;
            
            System.out.println("  文件大小: " + fileSize + " 字节");
            System.out.println("  传统I/O时间: " + traditionalTime / 1000000 + "ms");
            System.out.println("  内存映射时间: " + mappedTime / 1000000 + "ms");
            System.out.println("  性能提升: " + (traditionalTime / (double) mappedTime) + "倍");
            
        } catch (IOException e) {
            System.out.println("性能对比测试失败: " + e.getMessage());
        }
    }
}

14.7 文件压缩和解压

14.7.1 ZIP文件操作

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.zip.*;

// 文件压缩和解压演示
public class CompressionDemo {
    
    public static void main(String[] args) {
        // ZIP压缩演示
        demonstrateZipCompression();
        
        // ZIP解压演示
        demonstrateZipExtraction();
        
        // GZIP压缩演示
        demonstrateGzipCompression();
        
        // 压缩性能测试
        demonstrateCompressionPerformance();
    }
    
    // ZIP压缩演示
    public static void demonstrateZipCompression() {
        System.out.println("=== ZIP压缩演示 ===");
        
        // 创建测试文件
        createTestFiles();
        
        String zipFileName = "test_archive.zip";
        String[] filesToCompress = {"test1.txt", "test2.txt", "test3.txt"};
        
        try (ZipOutputStream zos = new ZipOutputStream(
                new FileOutputStream(zipFileName))) {
            
            for (String fileName : filesToCompress) {
                File file = new File(fileName);
                if (!file.exists()) continue;
                
                // 添加ZIP条目
                ZipEntry zipEntry = new ZipEntry(fileName);
                zos.putNextEntry(zipEntry);
                
                // 读取文件并写入ZIP
                try (FileInputStream fis = new FileInputStream(file)) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        zos.write(buffer, 0, bytesRead);
                    }
                }
                
                zos.closeEntry();
                System.out.println("已压缩文件: " + fileName);
            }
            
            System.out.println("ZIP压缩完成: " + zipFileName);
            
        } catch (IOException e) {
            System.out.println("ZIP压缩失败: " + e.getMessage());
        }
        
        // 显示压缩信息
        displayCompressionInfo(filesToCompress, zipFileName);
    }
    
    private static void createTestFiles() {
        String[] fileNames = {"test1.txt", "test2.txt", "test3.txt"};
        String[] contents = {
            "这是第一个测试文件\n包含一些文本内容\n用于压缩测试",
            "第二个文件的内容\n包含更多的文本\n用于演示ZIP压缩功能\n重复内容重复内容重复内容",
            "第三个文件\n包含大量重复文本\n" + "重复行\n".repeat(100)
        };
        
        for (int i = 0; i < fileNames.length; i++) {
            try {
                Files.write(Paths.get(fileNames[i]), contents[i].getBytes("UTF-8"));
            } catch (IOException e) {
                System.out.println("创建测试文件失败: " + fileNames[i]);
            }
        }
    }
    
    private static void displayCompressionInfo(String[] originalFiles, String zipFile) {
        try {
            long originalSize = 0;
            for (String fileName : originalFiles) {
                File file = new File(fileName);
                if (file.exists()) {
                    originalSize += file.length();
                }
            }
            
            long compressedSize = new File(zipFile).length();
            double compressionRatio = (1.0 - (double) compressedSize / originalSize) * 100;
            
            System.out.println("\n压缩信息:");
            System.out.println("  原始大小: " + originalSize + " 字节");
            System.out.println("  压缩大小: " + compressedSize + " 字节");
            System.out.println("  压缩率: " + String.format("%.1f%%", compressionRatio));
            
        } catch (Exception e) {
            System.out.println("获取压缩信息失败: " + e.getMessage());
        }
    }
    
    // ZIP解压演示
    public static void demonstrateZipExtraction() {
        System.out.println("\n=== ZIP解压演示 ===");
        
        String zipFileName = "test_archive.zip";
        String extractDir = "extracted";
        
        // 创建解压目录
        try {
            Files.createDirectories(Paths.get(extractDir));
        } catch (IOException e) {
            System.out.println("创建解压目录失败: " + e.getMessage());
            return;
        }
        
        try (ZipInputStream zis = new ZipInputStream(
                new FileInputStream(zipFileName))) {
            
            ZipEntry zipEntry;
            while ((zipEntry = zis.getNextEntry()) != null) {
                String fileName = zipEntry.getName();
                File extractedFile = new File(extractDir, fileName);
                
                System.out.println("正在解压: " + fileName);
                
                // 创建父目录
                extractedFile.getParentFile().mkdirs();
                
                // 解压文件
                try (FileOutputStream fos = new FileOutputStream(extractedFile)) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    
                    while ((bytesRead = zis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                }
                
                zis.closeEntry();
                
                // 显示文件信息
                System.out.println("  解压大小: " + extractedFile.length() + " 字节");
                System.out.println("  压缩大小: " + zipEntry.getCompressedSize() + " 字节");
                System.out.println("  压缩方法: " + getCompressionMethod(zipEntry.getMethod()));
            }
            
            System.out.println("ZIP解压完成");
            
        } catch (IOException e) {
            System.out.println("ZIP解压失败: " + e.getMessage());
        }
        
        // 验证解压结果
        verifyExtraction(extractDir);
    }
    
    private static String getCompressionMethod(int method) {
        switch (method) {
            case ZipEntry.STORED: return "STORED (无压缩)";
            case ZipEntry.DEFLATED: return "DEFLATED (压缩)";
            default: return "未知方法 (" + method + ")";
        }
    }
    
    private static void verifyExtraction(String extractDir) {
        System.out.println("\n验证解压结果:");
        
        try (Stream<Path> paths = Files.walk(Paths.get(extractDir))) {
            paths.filter(Files::isRegularFile)
                 .forEach(path -> {
                     try {
                         long size = Files.size(path);
                         System.out.println("  " + path.getFileName() + ": " + size + " 字节");
                     } catch (IOException e) {
                         System.out.println("  " + path.getFileName() + ": 获取大小失败");
                     }
                 });
        } catch (IOException e) {
            System.out.println("验证解压结果失败: " + e.getMessage());
        }
    }
    
    // GZIP压缩演示
    public static void demonstrateGzipCompression() {
        System.out.println("\n=== GZIP压缩演示 ===");
        
        String sourceFile = "test1.txt";
        String gzipFile = sourceFile + ".gz";
        
        // GZIP压缩
        try (FileInputStream fis = new FileInputStream(sourceFile);
             GZIPOutputStream gzos = new GZIPOutputStream(
                 new FileOutputStream(gzipFile))) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                gzos.write(buffer, 0, bytesRead);
            }
            
            System.out.println("GZIP压缩完成: " + gzipFile);
            
        } catch (IOException e) {
            System.out.println("GZIP压缩失败: " + e.getMessage());
            return;
        }
        
        // GZIP解压
        String decompressedFile = "decompressed_" + sourceFile;
        
        try (GZIPInputStream gzis = new GZIPInputStream(
                new FileInputStream(gzipFile));
             FileOutputStream fos = new FileOutputStream(decompressedFile)) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = gzis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            
            System.out.println("GZIP解压完成: " + decompressedFile);
            
        } catch (IOException e) {
            System.out.println("GZIP解压失败: " + e.getMessage());
            return;
        }
        
        // 比较文件大小
        try {
            long originalSize = Files.size(Paths.get(sourceFile));
            long compressedSize = Files.size(Paths.get(gzipFile));
            long decompressedSize = Files.size(Paths.get(decompressedFile));
            
            System.out.println("\nGZIP压缩信息:");
            System.out.println("  原始文件: " + originalSize + " 字节");
            System.out.println("  压缩文件: " + compressedSize + " 字节");
            System.out.println("  解压文件: " + decompressedSize + " 字节");
            System.out.println("  压缩率: " + String.format("%.1f%%", 
                (1.0 - (double) compressedSize / originalSize) * 100));
            System.out.println("  数据完整性: " + (originalSize == decompressedSize ? "正确" : "错误"));
            
        } catch (IOException e) {
            System.out.println("获取GZIP文件信息失败: " + e.getMessage());
        }
    }
    
    // 压缩性能测试
    public static void demonstrateCompressionPerformance() {
        System.out.println("\n=== 压缩性能测试 ===");
        
        // 创建大文件用于测试
        String testFile = "large_test.txt";
        createLargeTestFile(testFile, 100000); // 10万行
        
        try {
            long originalSize = Files.size(Paths.get(testFile));
            System.out.println("测试文件大小: " + formatBytes(originalSize));
            
            // 测试不同压缩级别的ZIP
            testZipCompressionLevels(testFile);
            
            // 测试GZIP压缩
            testGzipCompression(testFile);
            
        } catch (IOException e) {
            System.out.println("压缩性能测试失败: " + e.getMessage());
        }
    }
    
    private static void createLargeTestFile(String fileName, int lines) {
        try (PrintWriter pw = new PrintWriter(new FileWriter(fileName))) {
            for (int i = 0; i < lines; i++) {
                pw.println("这是第" + i + "行测试数据,包含一些重复的内容用于测试压缩效果。");
                if (i % 10 == 0) {
                    pw.println("重复行:" + "重复内容 ".repeat(20));
                }
            }
        } catch (IOException e) {
            System.out.println("创建大文件失败: " + e.getMessage());
        }
    }
    
    private static void testZipCompressionLevels(String sourceFile) {
        System.out.println("\nZIP压缩级别测试:");
        
        int[] levels = {Deflater.NO_COMPRESSION, Deflater.BEST_SPEED, 
                       Deflater.DEFAULT_COMPRESSION, Deflater.BEST_COMPRESSION};
        String[] levelNames = {"无压缩", "最快速度", "默认", "最佳压缩"};
        
        for (int i = 0; i < levels.length; i++) {
            String zipFile = "test_level_" + i + ".zip";
            
            long startTime = System.currentTimeMillis();
            
            try (ZipOutputStream zos = new ZipOutputStream(
                    new FileOutputStream(zipFile))) {
                
                zos.setLevel(levels[i]);
                
                ZipEntry entry = new ZipEntry(sourceFile);
                zos.putNextEntry(entry);
                
                Files.copy(Paths.get(sourceFile), zos);
                zos.closeEntry();
                
            } catch (IOException e) {
                System.out.println("  " + levelNames[i] + ": 压缩失败");
                continue;
            }
            
            long compressionTime = System.currentTimeMillis() - startTime;
            
            try {
                long originalSize = Files.size(Paths.get(sourceFile));
                long compressedSize = Files.size(Paths.get(zipFile));
                double ratio = (1.0 - (double) compressedSize / originalSize) * 100;
                
                System.out.printf("  %s: %s -> %s (%.1f%%) 耗时: %dms%n",
                    levelNames[i], formatBytes(originalSize), 
                    formatBytes(compressedSize), ratio, compressionTime);
                    
            } catch (IOException e) {
                System.out.println("  " + levelNames[i] + ": 获取文件信息失败");
            }
        }
    }
    
    private static void testGzipCompression(String sourceFile) {
        System.out.println("\nGZIP压缩测试:");
        
        String gzipFile = sourceFile + ".perf.gz";
        
        long startTime = System.currentTimeMillis();
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             GZIPOutputStream gzos = new GZIPOutputStream(
                 new FileOutputStream(gzipFile))) {
            
            byte[] buffer = new byte[8192];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                gzos.write(buffer, 0, bytesRead);
            }
            
        } catch (IOException e) {
            System.out.println("  GZIP压缩失败: " + e.getMessage());
            return;
        }
        
        long compressionTime = System.currentTimeMillis() - startTime;
        
        try {
            long originalSize = Files.size(Paths.get(sourceFile));
            long compressedSize = Files.size(Paths.get(gzipFile));
            double ratio = (1.0 - (double) compressedSize / originalSize) * 100;
            
            System.out.printf("  GZIP: %s -> %s (%.1f%%) 耗时: %dms%n",
                formatBytes(originalSize), formatBytes(compressedSize), 
                ratio, compressionTime);
                
        } catch (IOException e) {
            System.out.println("  GZIP: 获取文件信息失败");
        }
    }
    
    private static String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024));
        return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024));
    }
}

14.8 I/O最佳实践

14.8.1 性能优化和资源管理

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.concurrent.*;

// I/O最佳实践演示
public class IOBestPracticesDemo {
    
    public static void main(String[] args) {
        // 资源管理最佳实践
        demonstrateResourceManagement();
        
        // 缓冲区优化
        demonstrateBufferOptimization();
        
        // 异步I/O
        demonstrateAsyncIO();
        
        // 错误处理
        demonstrateErrorHandling();
        
        // 性能监控
        demonstratePerformanceMonitoring();
    }
    
    // 资源管理最佳实践
    public static void demonstrateResourceManagement() {
        System.out.println("=== 资源管理最佳实践 ===");
        
        // 1. 使用try-with-resources(推荐)
        System.out.println("1. try-with-resources模式:");
        
        try (BufferedReader reader = Files.newBufferedReader(Paths.get("test.txt"));
             BufferedWriter writer = Files.newBufferedWriter(Paths.get("output.txt"))) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line.toUpperCase());
                writer.newLine();
            }
            
            System.out.println("  文件处理完成,资源自动关闭");
            
        } catch (IOException e) {
            System.out.println("  文件处理失败: " + e.getMessage());
        }
        
        // 2. 自定义资源管理
        System.out.println("\n2. 自定义资源管理:");
        
        try (ManagedFileProcessor processor = new ManagedFileProcessor("test.txt")) {
            processor.processFile();
            System.out.println("  自定义资源处理完成");
        } catch (Exception e) {
            System.out.println("  自定义资源处理失败: " + e.getMessage());
        }
        
        // 3. 资源池管理
        demonstrateResourcePool();
    }
    
    // 自定义资源管理类
    static class ManagedFileProcessor implements AutoCloseable {
        private final BufferedReader reader;
        private final String fileName;
        
        public ManagedFileProcessor(String fileName) throws IOException {
            this.fileName = fileName;
            this.reader = Files.newBufferedReader(Paths.get(fileName));
        }
        
        public void processFile() throws IOException {
            String line;
            int lineCount = 0;
            while ((line = reader.readLine()) != null) {
                lineCount++;
                // 处理每一行
            }
            System.out.println("    处理了 " + lineCount + " 行数据");
        }
        
        @Override
        public void close() throws Exception {
            if (reader != null) {
                reader.close();
                System.out.println("    已关闭文件: " + fileName);
            }
        }
    }
    
    private static void demonstrateResourcePool() {
        System.out.println("\n3. 资源池管理:");
        
        // 简单的缓冲区池
        BufferPool bufferPool = new BufferPool(5, 1024);
        
        try {
            ByteBuffer buffer1 = bufferPool.acquire();
            ByteBuffer buffer2 = bufferPool.acquire();
            
            System.out.println("  获取缓冲区: " + buffer1.capacity() + " 字节");
            System.out.println("  获取缓冲区: " + buffer2.capacity() + " 字节");
            
            // 使用缓冲区...
            
            bufferPool.release(buffer1);
            bufferPool.release(buffer2);
            
            System.out.println("  缓冲区已归还到池中");
            
        } finally {
            bufferPool.shutdown();
        }
    }
    
    // 简单的缓冲区池实现
    static class BufferPool {
        private final BlockingQueue<ByteBuffer> pool;
        private final int bufferSize;
        
        public BufferPool(int poolSize, int bufferSize) {
            this.bufferSize = bufferSize;
            this.pool = new ArrayBlockingQueue<>(poolSize);
            
            // 预创建缓冲区
            for (int i = 0; i < poolSize; i++) {
                pool.offer(ByteBuffer.allocateDirect(bufferSize));
            }
        }
        
        public ByteBuffer acquire() throws InterruptedException {
            ByteBuffer buffer = pool.poll(1, TimeUnit.SECONDS);
            if (buffer == null) {
                // 池中没有可用缓冲区,创建新的
                buffer = ByteBuffer.allocateDirect(bufferSize);
            }
            buffer.clear();
            return buffer;
        }
        
        public void release(ByteBuffer buffer) {
            if (buffer != null) {
                buffer.clear();
                pool.offer(buffer);
            }
        }
        
        public void shutdown() {
            pool.clear();
        }
    }
    
    // 缓冲区优化
    public static void demonstrateBufferOptimization() {
        System.out.println("\n=== 缓冲区优化 ===");
        
        String testFile = "buffer_test.txt";
        createTestFile(testFile, 50000); // 创建测试文件
        
        // 测试不同缓冲区大小的性能
        int[] bufferSizes = {512, 1024, 4096, 8192, 16384, 32768};
        
        System.out.println("缓冲区大小性能测试:");
        
        for (int bufferSize : bufferSizes) {
            long startTime = System.nanoTime();
            
            try (FileInputStream fis = new FileInputStream(testFile)) {
                byte[] buffer = new byte[bufferSize];
                long totalBytes = 0;
                int bytesRead;
                
                while ((bytesRead = fis.read(buffer)) != -1) {
                    totalBytes += bytesRead;
                }
                
                long duration = System.nanoTime() - startTime;
                System.out.printf("  缓冲区 %6d 字节: %3d ms (读取 %d 字节)%n", 
                    bufferSize, duration / 1000000, totalBytes);
                    
            } catch (IOException e) {
                System.out.println("  缓冲区 " + bufferSize + " 字节: 测试失败");
            }
        }
        
        // 直接缓冲区 vs 堆缓冲区
        compareDirectVsHeapBuffer(testFile);
    }
    
    private static void createTestFile(String fileName, int lines) {
        try (PrintWriter pw = new PrintWriter(new FileWriter(fileName))) {
            for (int i = 0; i < lines; i++) {
                pw.println("这是第" + i + "行测试数据,用于缓冲区性能测试。");
            }
        } catch (IOException e) {
            System.out.println("创建测试文件失败: " + e.getMessage());
        }
    }
    
    private static void compareDirectVsHeapBuffer(String fileName) {
        System.out.println("\n直接缓冲区 vs 堆缓冲区:");
        
        int bufferSize = 8192;
        
        // 堆缓冲区测试
        long startTime = System.nanoTime();
        try (FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ)) {
            ByteBuffer heapBuffer = ByteBuffer.allocate(bufferSize);
            long totalBytes = 0;
            
            while (channel.read(heapBuffer) != -1) {
                totalBytes += heapBuffer.position();
                heapBuffer.clear();
            }
            
            long heapTime = System.nanoTime() - startTime;
            System.out.printf("  堆缓冲区: %d ms (读取 %d 字节)%n", 
                heapTime / 1000000, totalBytes);
                
        } catch (IOException e) {
            System.out.println("  堆缓冲区测试失败: " + e.getMessage());
        }
        
        // 直接缓冲区测试
        startTime = System.nanoTime();
        try (FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ)) {
            ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize);
            long totalBytes = 0;
            
            while (channel.read(directBuffer) != -1) {
                totalBytes += directBuffer.position();
                directBuffer.clear();
            }
            
            long directTime = System.nanoTime() - startTime;
            System.out.printf("  直接缓冲区: %d ms (读取 %d 字节)%n", 
                directTime / 1000000, totalBytes);
                
        } catch (IOException e) {
            System.out.println("  直接缓冲区测试失败: " + e.getMessage());
        }
    }
    
    // 异步I/O演示
    public static void demonstrateAsyncIO() {
        System.out.println("\n=== 异步I/O演示 ===");
        
        // 使用CompletableFuture实现异步文件读取
        CompletableFuture<String> asyncRead = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(100); // 模拟I/O延迟
                return Files.readString(Paths.get("test.txt"));
            } catch (Exception e) {
                throw new RuntimeException("异步读取失败", e);
            }
        });
        
        // 异步写入
        CompletableFuture<Void> asyncWrite = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(50); // 模拟I/O延迟
                Files.writeString(Paths.get("async_output.txt"), 
                    "异步写入的内容: " + System.currentTimeMillis());
            } catch (Exception e) {
                throw new RuntimeException("异步写入失败", e);
            }
        });
        
        // 组合异步操作
        CompletableFuture<String> combined = asyncRead
            .thenCompose(content -> {
                return CompletableFuture.supplyAsync(() -> {
                    return "处理后的内容: " + content.toUpperCase();
                });
            });
        
        try {
            // 等待所有异步操作完成
            CompletableFuture.allOf(asyncWrite, combined).get(5, TimeUnit.SECONDS);
            
            String result = combined.get();
            System.out.println("异步操作完成");
            System.out.println("处理结果长度: " + result.length());
            
        } catch (Exception e) {
            System.out.println("异步I/O操作失败: " + e.getMessage());
        }
    }
    
    // 错误处理最佳实践
    public static void demonstrateErrorHandling() {
        System.out.println("\n=== 错误处理最佳实践 ===");
        
        // 1. 具体的异常处理
        System.out.println("1. 具体异常处理:");
        
        try {
            processFileWithSpecificExceptions("nonexistent.txt");
        } catch (FileNotFoundException e) {
            System.out.println("  文件未找到: " + e.getMessage());
        } catch (SecurityException e) {
            System.out.println("  安全异常: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("  I/O异常: " + e.getMessage());
        }
        
        // 2. 重试机制
        System.out.println("\n2. 重试机制:");
        
        boolean success = retryOperation(() -> {
            // 模拟可能失败的操作
            if (Math.random() < 0.7) {
                throw new IOException("模拟I/O错误");
            }
            return "操作成功";
        }, 3, 1000);
        
        System.out.println("  重试操作结果: " + (success ? "成功" : "失败"));
        
        // 3. 优雅降级
        System.out.println("\n3. 优雅降级:");
        
        String content = readFileWithFallback("important.txt", "默认内容");
        System.out.println("  读取内容: " + content);
    }
    
    private static void processFileWithSpecificExceptions(String fileName) throws IOException {
        Path path = Paths.get(fileName);
        
        // 检查文件是否存在
        if (!Files.exists(path)) {
            throw new FileNotFoundException("文件不存在: " + fileName);
        }
        
        // 检查读取权限
        if (!Files.isReadable(path)) {
            throw new SecurityException("没有读取权限: " + fileName);
        }
        
        // 读取文件
        Files.readString(path);
    }
    
    private static boolean retryOperation(IOOperation operation, int maxRetries, long delayMs) {
        for (int attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                String result = operation.execute();
                System.out.println("    第" + attempt + "次尝试成功: " + result);
                return true;
            } catch (Exception e) {
                System.out.println("    第" + attempt + "次尝试失败: " + e.getMessage());
                
                if (attempt < maxRetries) {
                    try {
                        Thread.sleep(delayMs);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
        return false;
    }
    
    @FunctionalInterface
    interface IOOperation {
        String execute() throws Exception;
    }
    
    private static String readFileWithFallback(String fileName, String fallbackContent) {
        try {
            return Files.readString(Paths.get(fileName));
        } catch (IOException e) {
            System.out.println("    读取文件失败,使用默认内容: " + e.getMessage());
            return fallbackContent;
        }
    }
    
    // 性能监控
    public static void demonstratePerformanceMonitoring() {
        System.out.println("\n=== 性能监控 ===");
        
        // I/O性能监控器
        IOPerformanceMonitor monitor = new IOPerformanceMonitor();
        
        String testFile = "performance_test.txt";
        
        // 监控文件写入
        monitor.startOperation("文件写入");
        try (PrintWriter pw = new PrintWriter(new FileWriter(testFile))) {
            for (int i = 0; i < 10000; i++) {
                pw.println("测试行 " + i);
            }
        } catch (IOException e) {
            System.out.println("写入失败: " + e.getMessage());
        }
        monitor.endOperation("文件写入");
        
        // 监控文件读取
        monitor.startOperation("文件读取");
        try (BufferedReader br = new BufferedReader(new FileReader(testFile))) {
            String line;
            int lineCount = 0;
            while ((line = br.readLine()) != null) {
                lineCount++;
            }
            System.out.println("读取了 " + lineCount + " 行");
        } catch (IOException e) {
            System.out.println("读取失败: " + e.getMessage());
        }
        monitor.endOperation("文件读取");
        
        // 显示性能统计
        monitor.printStatistics();
    }
    
    // 简单的性能监控器
    static class IOPerformanceMonitor {
        private final Map<String, Long> startTimes = new HashMap<>();
        private final Map<String, Long> durations = new HashMap<>();
        private final Map<String, Integer> counts = new HashMap<>();
        
        public void startOperation(String operationName) {
            startTimes.put(operationName, System.nanoTime());
        }
        
        public void endOperation(String operationName) {
            Long startTime = startTimes.remove(operationName);
            if (startTime != null) {
                long duration = System.nanoTime() - startTime;
                durations.merge(operationName, duration, Long::sum);
                counts.merge(operationName, 1, Integer::sum);
            }
        }
        
        public void printStatistics() {
            System.out.println("\n性能统计:");
            for (String operation : durations.keySet()) {
                long totalDuration = durations.get(operation);
                int count = counts.get(operation);
                long avgDuration = totalDuration / count;
                
                System.out.printf("  %s: 总耗时=%dms, 次数=%d, 平均=%dms%n",
                    operation, totalDuration / 1000000, count, avgDuration / 1000000);
            }
        }
    }
}

14.9 本章小结

14.9.1 I/O技术对比

技术类型 适用场景 优点 缺点
字节流 二进制数据、图片、音频 通用性强、效率高 不适合文本处理
字符流 文本文件、配置文件 编码处理、易读性 只适合文本数据
缓冲流 频繁读写操作 性能优化显著 内存占用增加
NIO 大文件、高并发 内存映射、零拷贝 编程复杂度高
序列化 对象持久化、网络传输 对象完整性 版本兼容性问题

14.9.2 性能优化要点

  1. 选择合适的流类型

    • 二进制数据使用字节流
    • 文本数据使用字符流
    • 频繁操作使用缓冲流
  2. 缓冲区大小优化

    • 根据数据量选择合适的缓冲区大小
    • 一般推荐8KB-32KB
    • 大文件可以使用更大的缓冲区
  3. 资源管理

    • 使用try-with-resources自动关闭资源
    • 及时释放不再使用的流
    • 考虑使用资源池管理重复使用的资源
  4. NIO优化

    • 大文件使用内存映射
    • 批量操作使用Channel.transferTo()
    • 高并发场景使用异步I/O

14.9.3 安全考虑

  1. 路径安全

    • 验证文件路径,防止路径遍历攻击
    • 限制文件访问范围
    • 检查文件权限
  2. 资源限制

    • 限制文件大小
    • 控制并发I/O操作数量
    • 设置超时时间
  3. 数据完整性

    • 使用校验和验证数据完整性
    • 实现重试机制
    • 提供回滚功能

下一章预告

在下一章中,我们将学习Java数据库编程,包括: - JDBC基础操作 - 连接池管理 - 事务处理 - ORM框架使用 - 数据库性能优化

练习题

基础练习

  1. 文件复制工具

    • 实现一个文件复制工具,支持进度显示
    • 比较不同复制方法的性能
    • 添加错误处理和重试机制
  2. 日志文件分析器

    • 读取大型日志文件
    • 统计不同级别的日志数量
    • 提取错误信息并保存到单独文件
  3. 配置文件管理器

    • 实现Properties文件的读写
    • 支持配置项的增删改查
    • 提供配置变更监听功能

进阶练习

  1. 文件压缩工具

    • 实现目录的递归压缩
    • 支持多种压缩格式
    • 提供压缩进度和统计信息
  2. 对象序列化框架

    • 实现自定义序列化协议
    • 支持版本兼容性检查
    • 提供序列化性能优化
  3. 高性能文件服务器

    • 使用NIO实现文件服务器
    • 支持断点续传
    • 实现文件缓存机制

挑战练习

  1. 分布式文件系统客户端

    • 实现文件的分片存储
    • 支持数据冗余和恢复
    • 提供一致性保证
  2. 实时日志监控系统

    • 监控多个日志文件的变化
    • 实时分析和告警
    • 支持分布式部署

通过这些练习,你将深入掌握Java I/O编程的各个方面,为后续的数据库编程和网络编程打下坚实基础。

14.2 字节流操作

14.2.1 基础字节流

import java.io.*;
import java.util.*;

// 字节流操作演示
public class ByteStreamDemo {
    
    public static void main(String[] args) {
        // 文件字节流操作
        demonstrateFileByteStream();
        
        // 字节数组流操作
        demonstrateByteArrayStream();
        
        // 缓冲字节流操作
        demonstrateBufferedByteStream();
        
        // 数据流操作
        demonstrateDataStream();
    }
    
    // 文件字节流操作
    public static void demonstrateFileByteStream() {
        System.out.println("=== 文件字节流操作 ===");
        
        String fileName = "byte_test.txt";
        String content = "Hello, 这是字节流测试内容!";
        
        // 写入文件
        try (FileOutputStream fos = new FileOutputStream(fileName)) {
            byte[] bytes = content.getBytes("UTF-8");
            fos.write(bytes);
            System.out.println("写入文件成功,字节数: " + bytes.length);
        } catch (IOException e) {
            System.out.println("写入文件失败: " + e.getMessage());
        }
        
        // 读取文件
        try (FileInputStream fis = new FileInputStream(fileName)) {
            byte[] buffer = new byte[1024];
            int bytesRead = fis.read(buffer);
            
            if (bytesRead > 0) {
                String readContent = new String(buffer, 0, bytesRead, "UTF-8");
                System.out.println("读取内容: " + readContent);
                System.out.println("读取字节数: " + bytesRead);
            }
        } catch (IOException e) {
            System.out.println("读取文件失败: " + e.getMessage());
        }
        
        // 逐字节读取
        try (FileInputStream fis = new FileInputStream(fileName)) {
            System.out.println("\n逐字节读取:");
            int byteValue;
            int count = 0;
            while ((byteValue = fis.read()) != -1 && count < 10) {
                System.out.printf("字节[%d]: %d (字符: %c)%n", count, byteValue, (char)byteValue);
                count++;
            }
        } catch (IOException e) {
            System.out.println("逐字节读取失败: " + e.getMessage());
        }
    }
    
    // 字节数组流操作
    public static void demonstrateByteArrayStream() {
        System.out.println("\n=== 字节数组流操作 ===");
        
        // ByteArrayOutputStream写入数据
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
        try {
            String data1 = "第一段数据";
            String data2 = "第二段数据";
            
            baos.write(data1.getBytes("UTF-8"));
            baos.write('\n');
            baos.write(data2.getBytes("UTF-8"));
            
            byte[] result = baos.toByteArray();
            System.out.println("写入的数据: " + new String(result, "UTF-8"));
            System.out.println("总字节数: " + result.length);
            
        } catch (IOException e) {
            System.out.println("字节数组输出流操作失败: " + e.getMessage());
        }
        
        // ByteArrayInputStream读取数据
        byte[] sourceData = "Hello ByteArrayInputStream!".getBytes();
        
        try (ByteArrayInputStream bais = new ByteArrayInputStream(sourceData)) {
            byte[] buffer = new byte[5];
            int bytesRead;
            
            System.out.println("\n分块读取字节数组:");
            while ((bytesRead = bais.read(buffer)) != -1) {
                String chunk = new String(buffer, 0, bytesRead);
                System.out.println("读取块: " + chunk + " (" + bytesRead + " 字节)");
            }
        } catch (IOException e) {
            System.out.println("字节数组输入流操作失败: " + e.getMessage());
        }
    }
    
    // 缓冲字节流操作
    public static void demonstrateBufferedByteStream() {
        System.out.println("\n=== 缓冲字节流操作 ===");
        
        String fileName = "buffered_test.txt";
        
        // 性能对比:普通流 vs 缓冲流
        byte[] testData = new byte[10000];
        Arrays.fill(testData, (byte) 'A');
        
        // 普通流写入
        long startTime = System.currentTimeMillis();
        try (FileOutputStream fos = new FileOutputStream(fileName + ".normal")) {
            for (byte b : testData) {
                fos.write(b);
            }
        } catch (IOException e) {
            System.out.println("普通流写入失败: " + e.getMessage());
        }
        long normalTime = System.currentTimeMillis() - startTime;
        
        // 缓冲流写入
        startTime = System.currentTimeMillis();
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(fileName + ".buffered"))) {
            for (byte b : testData) {
                bos.write(b);
            }
        } catch (IOException e) {
            System.out.println("缓冲流写入失败: " + e.getMessage());
        }
        long bufferedTime = System.currentTimeMillis() - startTime;
        
        System.out.println("普通流写入时间: " + normalTime + "ms");
        System.out.println("缓冲流写入时间: " + bufferedTime + "ms");
        System.out.println("性能提升: " + (normalTime / (double) bufferedTime) + "倍");
        
        // 缓冲流的缓冲区大小设置
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream(fileName + ".buffered"), 8192)) {
            
            byte[] buffer = new byte[1024];
            int totalBytes = 0;
            int bytesRead;
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                totalBytes += bytesRead;
            }
            
            System.out.println("\n缓冲流读取总字节数: " + totalBytes);
        } catch (IOException e) {
            System.out.println("缓冲流读取失败: " + e.getMessage());
        }
    }
    
    // 数据流操作
    public static void demonstrateDataStream() {
        System.out.println("\n=== 数据流操作 ===");
        
        String fileName = "data_test.dat";
        
        // 写入各种数据类型
        try (DataOutputStream dos = new DataOutputStream(
                new FileOutputStream(fileName))) {
            
            dos.writeBoolean(true);
            dos.writeByte(127);
            dos.writeShort(32767);
            dos.writeInt(2147483647);
            dos.writeLong(9223372036854775807L);
            dos.writeFloat(3.14159f);
            dos.writeDouble(2.718281828459045);
            dos.writeUTF("Hello DataStream!");
            
            System.out.println("数据写入完成");
            
        } catch (IOException e) {
            System.out.println("数据写入失败: " + e.getMessage());
        }
        
        // 读取各种数据类型
        try (DataInputStream dis = new DataInputStream(
                new FileInputStream(fileName))) {
            
            boolean boolValue = dis.readBoolean();
            byte byteValue = dis.readByte();
            short shortValue = dis.readShort();
            int intValue = dis.readInt();
            long longValue = dis.readLong();
            float floatValue = dis.readFloat();
            double doubleValue = dis.readDouble();
            String stringValue = dis.readUTF();
            
            System.out.println("\n读取的数据:");
            System.out.println("  boolean: " + boolValue);
            System.out.println("  byte: " + byteValue);
            System.out.println("  short: " + shortValue);
            System.out.println("  int: " + intValue);
            System.out.println("  long: " + longValue);
            System.out.println("  float: " + floatValue);
            System.out.println("  double: " + doubleValue);
            System.out.println("  String: " + stringValue);
            
        } catch (IOException e) {
            System.out.println("数据读取失败: " + e.getMessage());
        }
        
        // 文件大小检查
        File dataFile = new File(fileName);
        if (dataFile.exists()) {
            System.out.println("\n数据文件大小: " + dataFile.length() + " 字节");
        }
    }
}

14.3 字符流操作

14.3.1 基础字符流

import java.io.*;
import java.nio.charset.*;
import java.util.*;

// 字符流操作演示
public class CharacterStreamDemo {
    
    public static void main(String[] args) {
        // 文件字符流操作
        demonstrateFileCharacterStream();
        
        // 字符编码处理
        demonstrateCharacterEncoding();
        
        // 缓冲字符流操作
        demonstrateBufferedCharacterStream();
        
        // 字符串流操作
        demonstrateStringStream();
        
        // 打印流操作
        demonstratePrintStream();
    }
    
    // 文件字符流操作
    public static void demonstrateFileCharacterStream() {
        System.out.println("=== 文件字符流操作 ===");
        
        String fileName = "character_test.txt";
        String content = "Hello World!\n你好,世界!\n这是字符流测试。";
        
        // 写入文件
        try (FileWriter fw = new FileWriter(fileName)) {
            fw.write(content);
            System.out.println("字符写入成功");
        } catch (IOException e) {
            System.out.println("字符写入失败: " + e.getMessage());
        }
        
        // 读取文件
        try (FileReader fr = new FileReader(fileName)) {
            char[] buffer = new char[1024];
            int charsRead = fr.read(buffer);
            
            if (charsRead > 0) {
                String readContent = new String(buffer, 0, charsRead);
                System.out.println("读取内容:");
                System.out.println(readContent);
                System.out.println("读取字符数: " + charsRead);
            }
        } catch (IOException e) {
            System.out.println("字符读取失败: " + e.getMessage());
        }
        
        // 逐字符读取
        try (FileReader fr = new FileReader(fileName)) {
            System.out.println("\n逐字符读取前10个字符:");
            int charValue;
            int count = 0;
            while ((charValue = fr.read()) != -1 && count < 10) {
                char ch = (char) charValue;
                System.out.printf("字符[%d]: %c (Unicode: %d)%n", count, ch, charValue);
                count++;
            }
        } catch (IOException e) {
            System.out.println("逐字符读取失败: " + e.getMessage());
        }
    }
    
    // 字符编码处理
    public static void demonstrateCharacterEncoding() {
        System.out.println("\n=== 字符编码处理 ===");
        
        String text = "Hello 世界 🌍";
        String fileName = "encoding_test";
        
        // 不同编码写入
        String[] encodings = {"UTF-8", "UTF-16", "GBK", "ISO-8859-1"};
        
        for (String encoding : encodings) {
            try {
                String file = fileName + "_" + encoding.replace("-", "") + ".txt";
                
                // 使用指定编码写入
                try (OutputStreamWriter osw = new OutputStreamWriter(
                        new FileOutputStream(file), encoding)) {
                    osw.write(text);
                }
                
                // 读取并显示
                try (InputStreamReader isr = new InputStreamReader(
                        new FileInputStream(file), encoding)) {
                    char[] buffer = new char[1024];
                    int charsRead = isr.read(buffer);
                    String readText = new String(buffer, 0, charsRead);
                    
                    File f = new File(file);
                    System.out.printf("编码: %-10s 文件大小: %d 字节 内容: %s%n", 
                        encoding, f.length(), readText);
                }
                
            } catch (IOException e) {
                System.out.println(encoding + " 编码处理失败: " + e.getMessage());
            } catch (UnsupportedEncodingException e) {
                System.out.println("不支持的编码: " + encoding);
            }
        }
        
        // 检测系统默认编码
        System.out.println("\n系统信息:");
        System.out.println("默认字符集: " + Charset.defaultCharset());
        System.out.println("文件编码: " + System.getProperty("file.encoding"));
        
        // 列出所有可用字符集
        System.out.println("\n可用字符集 (前10个):");
        Charset.availableCharsets().entrySet().stream()
            .limit(10)
            .forEach(entry -> System.out.println("  " + entry.getKey() + ": " + entry.getValue()));
    }
    
    // 缓冲字符流操作
    public static void demonstrateBufferedCharacterStream() {
        System.out.println("\n=== 缓冲字符流操作 ===");
        
        String fileName = "buffered_char_test.txt";
        
        // 使用BufferedWriter写入
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName))) {
            bw.write("第一行文本");
            bw.newLine();
            bw.write("第二行文本");
            bw.newLine();
            bw.write("第三行文本");
            bw.flush(); // 强制刷新缓冲区
            
            System.out.println("缓冲字符写入完成");
        } catch (IOException e) {
            System.out.println("缓冲字符写入失败: " + e.getMessage());
        }
        
        // 使用BufferedReader读取
        try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
            System.out.println("\n逐行读取:");
            String line;
            int lineNumber = 1;
            
            while ((line = br.readLine()) != null) {
                System.out.println("第" + lineNumber + "行: " + line);
                lineNumber++;
            }
        } catch (IOException e) {
            System.out.println("缓冲字符读取失败: " + e.getMessage());
        }
        
        // 性能测试:缓冲 vs 非缓冲
        performanceTest();
    }
    
    private static void performanceTest() {
        String fileName = "performance_test.txt";
        int iterations = 10000;
        
        // 非缓冲写入
        long startTime = System.currentTimeMillis();
        try (FileWriter fw = new FileWriter(fileName + ".normal")) {
            for (int i = 0; i < iterations; i++) {
                fw.write("Line " + i + "\n");
            }
        } catch (IOException e) {
            System.out.println("非缓冲写入失败: " + e.getMessage());
        }
        long normalTime = System.currentTimeMillis() - startTime;
        
        // 缓冲写入
        startTime = System.currentTimeMillis();
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName + ".buffered"))) {
            for (int i = 0; i < iterations; i++) {
                bw.write("Line " + i);
                bw.newLine();
            }
        } catch (IOException e) {
            System.out.println("缓冲写入失败: " + e.getMessage());
        }
        long bufferedTime = System.currentTimeMillis() - startTime;
        
        System.out.println("\n性能测试结果 (" + iterations + " 次写入):");
        System.out.println("非缓冲时间: " + normalTime + "ms");
        System.out.println("缓冲时间: " + bufferedTime + "ms");
        System.out.println("性能提升: " + (normalTime / (double) bufferedTime) + "倍");
    }
    
    // 字符串流操作
    public static void demonstrateStringStream() {
        System.out.println("\n=== 字符串流操作 ===");
        
        // StringWriter - 将字符写入字符串
        StringWriter sw = new StringWriter();
        
        try {
            sw.write("Hello ");
            sw.write("StringWriter!");
            sw.write("\n");
            sw.write("这是第二行");
            
            String result = sw.toString();
            System.out.println("StringWriter结果:");
            System.out.println(result);
            
            // 获取内部缓冲区
            StringBuffer buffer = sw.getBuffer();
            System.out.println("缓冲区长度: " + buffer.length());
            
        } catch (IOException e) {
            System.out.println("StringWriter操作失败: " + e.getMessage());
        }
        
        // StringReader - 从字符串读取字符
        String sourceText = "Hello StringReader!\n这是测试文本\n第三行";
        
        try (StringReader sr = new StringReader(sourceText)) {
            System.out.println("\nStringReader逐字符读取前20个字符:");
            
            for (int i = 0; i < 20; i++) {
                int ch = sr.read();
                if (ch == -1) break;
                
                char character = (char) ch;
                if (character == '\n') {
                    System.out.print("\\n ");
                } else {
                    System.out.print(character + " ");
                }
            }
            System.out.println();
        } catch (IOException e) {
            System.out.println("StringReader操作失败: " + e.getMessage());
        }
        
        // 使用BufferedReader读取StringReader
        try (BufferedReader br = new BufferedReader(new StringReader(sourceText))) {
            System.out.println("\n使用BufferedReader逐行读取StringReader:");
            String line;
            int lineNum = 1;
            
            while ((line = br.readLine()) != null) {
                System.out.println("行" + lineNum + ": " + line);
                lineNum++;
            }
        } catch (IOException e) {
            System.out.println("BufferedReader读取StringReader失败: " + e.getMessage());
        }
    }
    
    // 打印流操作
    public static void demonstratePrintStream() {
        System.out.println("\n=== 打印流操作 ===");
        
        String fileName = "print_test.txt";
        
        // PrintWriter操作
        try (PrintWriter pw = new PrintWriter(new FileWriter(fileName))) {
            // 各种打印方法
            pw.println("这是一行文本");
            pw.print("数字: ");
            pw.println(42);
            pw.printf("格式化输出: %s = %.2f%n", "π", Math.PI);
            pw.println("布尔值: " + true);
            
            // 打印对象
            Date now = new Date();
            pw.println("当前时间: " + now);
            
            // 检查错误状态
            if (pw.checkError()) {
                System.out.println("PrintWriter发生错误");
            } else {
                System.out.println("PrintWriter写入成功");
            }
            
        } catch (IOException e) {
            System.out.println("PrintWriter操作失败: " + e.getMessage());
        }
        
        // 读取并显示写入的内容
        try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
            System.out.println("\n读取PrintWriter写入的内容:");
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println("  " + line);
            }
        } catch (IOException e) {
            System.out.println("读取PrintWriter文件失败: " + e.getMessage());
        }
        
        // PrintStream操作(重定向System.out)
        try {
            // 保存原始System.out
            PrintStream originalOut = System.out;
            
            // 重定向到文件
            PrintStream fileOut = new PrintStream(new FileOutputStream("system_out.txt"));
            System.setOut(fileOut);
            
            // 这些输出会写入文件
            System.out.println("这行输出被重定向到文件");
            System.out.printf("格式化输出: %d + %d = %d%n", 1, 2, 3);
            
            // 恢复原始输出
            System.setOut(originalOut);
            fileOut.close();
            
            System.out.println("System.out重定向演示完成");
            
        } catch (IOException e) {
            System.out.println("PrintStream重定向失败: " + e.getMessage());
        }
    }
}

14.4 文件操作进阶

14.4.1 文件复制和移动

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

// 文件操作进阶演示
public class AdvancedFileOperationsDemo {
    
    public static void main(String[] args) {
        // 文件复制操作
        demonstrateFileCopy();
        
        // 目录操作
        demonstrateDirectoryOperations();
        
        // 文件属性操作
        demonstrateFileAttributes();
        
        // 文件监控
        demonstrateFileWatcher();
        
        // 临时文件操作
        demonstrateTempFiles();
    }
    
    // 文件复制操作
    public static void demonstrateFileCopy() {
        System.out.println("=== 文件复制操作 ===");
        
        // 创建测试文件
        String sourceFile = "source.txt";
        String content = "这是要复制的文件内容\n包含多行文本\n用于测试文件复制功能";
        
        try (FileWriter fw = new FileWriter(sourceFile)) {
            fw.write(content);
            System.out.println("创建源文件: " + sourceFile);
        } catch (IOException e) {
            System.out.println("创建源文件失败: " + e.getMessage());
            return;
        }
        
        // 方法1: 使用字节流复制
        copyFileUsingByteStream(sourceFile, "copy1_byte.txt");
        
        // 方法2: 使用字符流复制
        copyFileUsingCharacterStream(sourceFile, "copy2_char.txt");
        
        // 方法3: 使用缓冲流复制
        copyFileUsingBufferedStream(sourceFile, "copy3_buffered.txt");
        
        // 方法4: 使用NIO复制
        copyFileUsingNIO(sourceFile, "copy4_nio.txt");
        
        // 验证复制结果
        verifyFileCopies(sourceFile, Arrays.asList(
            "copy1_byte.txt", "copy2_char.txt", "copy3_buffered.txt", "copy4_nio.txt"
        ));
    }
    
    private static void copyFileUsingByteStream(String source, String target) {
        long startTime = System.currentTimeMillis();
        
        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(target)) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("字节流复制完成: " + target + " (耗时: " + duration + "ms)");
            
        } catch (IOException e) {
            System.out.println("字节流复制失败: " + e.getMessage());
        }
    }
    
    private static void copyFileUsingCharacterStream(String source, String target) {
        long startTime = System.currentTimeMillis();
        
        try (FileReader fr = new FileReader(source);
             FileWriter fw = new FileWriter(target)) {
            
            char[] buffer = new char[1024];
            int charsRead;
            
            while ((charsRead = fr.read(buffer)) != -1) {
                fw.write(buffer, 0, charsRead);
            }
            
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("字符流复制完成: " + target + " (耗时: " + duration + "ms)");
            
        } catch (IOException e) {
            System.out.println("字符流复制失败: " + e.getMessage());
        }
    }
    
    private static void copyFileUsingBufferedStream(String source, String target) {
        long startTime = System.currentTimeMillis();
        
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {
            
            byte[] buffer = new byte[8192];
            int bytesRead;
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("缓冲流复制完成: " + target + " (耗时: " + duration + "ms)");
            
        } catch (IOException e) {
            System.out.println("缓冲流复制失败: " + e.getMessage());
        }
    }
    
    private static void copyFileUsingNIO(String source, String target) {
        long startTime = System.currentTimeMillis();
        
        try {
            Path sourcePath = Paths.get(source);
            Path targetPath = Paths.get(target);
            
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("NIO复制完成: " + target + " (耗时: " + duration + "ms)");
            
        } catch (IOException e) {
            System.out.println("NIO复制失败: " + e.getMessage());
        }
    }
    
    private static void verifyFileCopies(String original, List<String> copies) {
        System.out.println("\n验证复制结果:");
        
        try {
            String originalContent = Files.readString(Paths.get(original));
            long originalSize = Files.size(Paths.get(original));
            
            System.out.println("原文件大小: " + originalSize + " 字节");
            
            for (String copy : copies) {
                try {
                    String copyContent = Files.readString(Paths.get(copy));
                    long copySize = Files.size(Paths.get(copy));
                    
                    boolean contentMatch = originalContent.equals(copyContent);
                    boolean sizeMatch = originalSize == copySize;
                    
                    System.out.printf("  %s: 大小匹配=%s, 内容匹配=%s%n", 
                        copy, sizeMatch, contentMatch);
                        
                } catch (IOException e) {
                    System.out.println("  " + copy + ": 验证失败 - " + e.getMessage());
                }
            }
            
        } catch (IOException e) {
            System.out.println("读取原文件失败: " + e.getMessage());
        }
    }
    
    // 目录操作
    public static void demonstrateDirectoryOperations() {
        System.out.println("\n=== 目录操作 ===");
        
        String testDir = "test_directory";
        Path testPath = Paths.get(testDir);
        
        try {
            // 创建目录
            if (!Files.exists(testPath)) {
                Files.createDirectory(testPath);
                System.out.println("创建目录: " + testDir);
            }
            
            // 创建多级目录
            Path multiLevelPath = Paths.get(testDir, "level1", "level2", "level3");
            Files.createDirectories(multiLevelPath);
            System.out.println("创建多级目录: " + multiLevelPath);
            
            // 在目录中创建文件
            for (int i = 1; i <= 3; i++) {
                Path filePath = testPath.resolve("file" + i + ".txt");
                Files.write(filePath, ("这是文件" + i + "的内容").getBytes());
            }
            
            // 列出目录内容
            System.out.println("\n目录内容:");
            try (Stream<Path> paths = Files.list(testPath)) {
                paths.forEach(path -> {
                    try {
                        if (Files.isDirectory(path)) {
                            System.out.println("  [目录] " + path.getFileName());
                        } else {
                            long size = Files.size(path);
                            System.out.println("  [文件] " + path.getFileName() + " (" + size + " 字节)");
                        }
                    } catch (IOException e) {
                        System.out.println("  [错误] " + path.getFileName() + ": " + e.getMessage());
                    }
                });
            }
            
            // 递归遍历目录
            System.out.println("\n递归遍历目录:");
            try (Stream<Path> paths = Files.walk(testPath)) {
                paths.filter(Files::isRegularFile)
                     .forEach(path -> {
                         try {
                             long size = Files.size(path);
                             System.out.println("  " + path + " (" + size + " 字节)");
                         } catch (IOException e) {
                             System.out.println("  " + path + ": 获取大小失败");
                         }
                     });
            }
            
            // 查找文件
            System.out.println("\n查找.txt文件:");
            try (Stream<Path> paths = Files.find(testPath, 10, 
                    (path, attrs) -> path.toString().endsWith(".txt"))) {
                paths.forEach(path -> System.out.println("  找到: " + path));
            }
            
        } catch (IOException e) {
            System.out.println("目录操作失败: " + e.getMessage());
        }
    }
    
    // 文件属性操作
    public static void demonstrateFileAttributes() {
        System.out.println("\n=== 文件属性操作 ===");
        
        String fileName = "attributes_test.txt";
        Path filePath = Paths.get(fileName);
        
        try {
            // 创建测试文件
            Files.write(filePath, "测试文件属性".getBytes());
            
            // 基本属性
            BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class);
            
            System.out.println("基本文件属性:");
            System.out.println("  文件大小: " + attrs.size() + " 字节");
            System.out.println("  创建时间: " + attrs.creationTime());
            System.out.println("  最后修改时间: " + attrs.lastModifiedTime());
            System.out.println("  最后访问时间: " + attrs.lastAccessTime());
            System.out.println("  是否为目录: " + attrs.isDirectory());
            System.out.println("  是否为常规文件: " + attrs.isRegularFile());
            System.out.println("  是否为符号链接: " + attrs.isSymbolicLink());
            
            // 修改文件时间
            FileTime newTime = FileTime.fromMillis(System.currentTimeMillis() - 86400000); // 一天前
            Files.setLastModifiedTime(filePath, newTime);
            System.out.println("\n修改文件时间后:");
            System.out.println("  新的最后修改时间: " + Files.getLastModifiedTime(filePath));
            
            // 文件权限(在支持的系统上)
            try {
                Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(filePath);
                System.out.println("\nPOSIX文件权限:");
                permissions.forEach(perm -> System.out.println("  " + perm));
            } catch (UnsupportedOperationException e) {
                System.out.println("\n当前系统不支持POSIX文件权限");
            }
            
            // 文件存储信息
            FileStore store = Files.getFileStore(filePath);
            System.out.println("\n文件存储信息:");
            System.out.println("  存储名称: " + store.name());
            System.out.println("  文件系统类型: " + store.type());
            System.out.println("  总空间: " + formatBytes(store.getTotalSpace()));
            System.out.println("  可用空间: " + formatBytes(store.getUsableSpace()));
            System.out.println("  未分配空间: " + formatBytes(store.getUnallocatedSpace()));
            
        } catch (IOException e) {
            System.out.println("文件属性操作失败: " + e.getMessage());
        }
    }
    
    private static String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024));
        return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024));
    }
    
    // 文件监控
    public static void demonstrateFileWatcher() {
        System.out.println("\n=== 文件监控 ===");
        
        Path watchDir = Paths.get("watch_test");
        
        try {
            // 创建监控目录
            if (!Files.exists(watchDir)) {
                Files.createDirectory(watchDir);
            }
            
            // 创建文件监控服务
            WatchService watchService = FileSystems.getDefault().newWatchService();
            
            // 注册监控事件
            watchDir.register(watchService, 
                StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY);
            
            System.out.println("开始监控目录: " + watchDir);
            System.out.println("将在5秒后创建、修改、删除文件进行测试...");
            
            // 启动监控线程
            Thread watchThread = new Thread(() -> {
                try {
                    while (true) {
                        WatchKey key = watchService.take();
                        
                        for (WatchEvent<?> event : key.pollEvents()) {
                            WatchEvent.Kind<?> kind = event.kind();
                            Path fileName = (Path) event.context();
                            
                            System.out.println("检测到文件变化: " + kind + " - " + fileName);
                        }
                        
                        if (!key.reset()) {
                            break;
                        }
                    }
                } catch (InterruptedException e) {
                    System.out.println("文件监控被中断");
                }
            });
            
            watchThread.setDaemon(true);
            watchThread.start();
            
            // 模拟文件操作
            Thread.sleep(1000);
            
            Path testFile = watchDir.resolve("test_watch.txt");
            
            // 创建文件
            Files.write(testFile, "初始内容".getBytes());
            Thread.sleep(500);
            
            // 修改文件
            Files.write(testFile, "修改后的内容".getBytes());
            Thread.sleep(500);
            
            // 删除文件
            Files.delete(testFile);
            Thread.sleep(500);
            
            System.out.println("文件监控演示完成");
            
        } catch (IOException | InterruptedException e) {
            System.out.println("文件监控失败: " + e.getMessage());
        }
    }
    
    // 临时文件操作
    public static void demonstrateTempFiles() {
        System.out.println("\n=== 临时文件操作 ===");
        
        try {
            // 创建临时文件
            Path tempFile = Files.createTempFile("demo", ".tmp");
            System.out.println("创建临时文件: " + tempFile);
            
            // 写入临时文件
            String content = "这是临时文件的内容\n临时文件会在程序结束时自动删除";
            Files.write(tempFile, content.getBytes());
            
            // 读取临时文件
            String readContent = Files.readString(tempFile);
            System.out.println("临时文件内容:");
            System.out.println(readContent);
            
            // 设置删除钩子
            tempFile.toFile().deleteOnExit();
            System.out.println("已设置程序退出时删除临时文件");
            
            // 创建临时目录
            Path tempDir = Files.createTempDirectory("demo_dir");
            System.out.println("创建临时目录: " + tempDir);
            
            // 在临时目录中创建文件
            Path tempFileInDir = tempDir.resolve("temp_file.txt");
            Files.write(tempFileInDir, "临时目录中的文件".getBytes());
            
            // 列出临时目录内容
            try (Stream<Path> paths = Files.list(tempDir)) {
                System.out.println("临时目录内容:");
                paths.forEach(path -> System.out.println("  " + path.getFileName()));
            }
            
            // 获取系统临时目录
            String systemTempDir = System.getProperty("java.io.tmpdir");
            System.out.println("\n系统临时目录: " + systemTempDir);
            
            // 清理临时目录
            Files.deleteIfExists(tempFileInDir);
            Files.deleteIfExists(tempDir);
            System.out.println("清理临时目录完成");
            
        } catch (IOException e) {
            System.out.println("临时文件操作失败: " + e.getMessage());
        }
    }
}