Java实践 (五:IO流、文件操作、序列化完整案例项目)

32 阅读8分钟

IO流、文件操作、序列化完整案例项目

项目结构

src/
├── main/
│   ├── java/
│   │   ├── file/
│   │   │   ├── FileManager.java
│   │   │   └── FileOperationsDemo.java
│   │   ├── io/
│   │   │   ├── ByteStreamDemo.java
│   │   │   ├── CharacterStreamDemo.java
│   │   │   └── BufferedStreamDemo.java
│   │   ├── serialization/
│   │   │   ├── Employee.java
│   │   │   ├── SerializationDemo.java
│   │   │   └── CustomSerializationDemo.java
│   │   └── MainApplication.java
│   └── resources/
│       ├── input.txt
│       ├── output.txt
│       └── data/
└── test/
    └── java/

核心类实现

1. 序列化实体类 - Employee.java

package serialization;

import java.io.Serializable;
import java.time.LocalDate;

public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String name;
    private String department;
    private Double salary;
    private transient LocalDate hireDate; // 不参与序列化
    
    public Employee() {}
    
    public Employee(Long id, String name, String department, Double salary, LocalDate hireDate) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.salary = salary;
        this.hireDate = hireDate;
    }
    
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getDepartment() { return department; }
    public void setDepartment(String department) { this.department = department; }
    
    public Double getSalary() { return salary; }
    public void setSalary(Double salary) { this.salary = salary; }
    
    public LocalDate getHireDate() { return hireDate; }
    public void setHireDate(LocalDate hireDate) { this.hireDate = hireDate; }
    
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", department='" + department + '\'' +
                ", salary=" + salary +
                ", hireDate=" + hireDate +
                '}';
    }
}

2. 字节流操作 - ByteStreamDemo.java

package io;

import java.io.*;

public class ByteStreamDemo {
    
    /**
     * 使用FileInputStream和FileOutputStream进行文件复制
     */
    public static void copyFileWithByteStream(String sourceFile, String targetFile) {
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(targetFile)) {
            
            int byteData;
            while ((byteData = fis.read()) != -1) {
                fos.write(byteData);
            }
            System.out.println("字节流文件复制完成");
            
        } catch (IOException e) {
            System.err.println("字节流操作异常: " + e.getMessage());
        }
    }
    
    /**
     * 使用缓冲字节流提高效率
     */
    public static void copyFileWithBufferedByteStream(String sourceFile, String targetFile) {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetFile))) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            System.out.println("缓冲字节流文件复制完成");
            
        } catch (IOException e) {
            System.err.println("缓冲字节流操作异常: " + e.getMessage());
        }
    }
    
    /**
     * 读取二进制文件并显示十六进制内容
     */
    public static void readBinaryFileAsHex(String fileName) {
        try (FileInputStream fis = new FileInputStream(fileName)) {
            
            int byteData;
            int count = 0;
            System.out.println("文件十六进制内容:");
            while ((byteData = fis.read()) != -1) {
                System.out.printf("%02X ", byteData);
                count++;
                if (count % 16 == 0) {
                    System.out.println();
                }
            }
            System.out.println();
            
        } catch (IOException e) {
            System.err.println("读取二进制文件异常: " + e.getMessage());
        }
    }
}

3. 字符流操作 - CharacterStreamDemo.java

package io;

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

public class CharacterStreamDemo {
    
    /**
     * 使用FileReader和FileWriter进行文本文件读写
     */
    public static void writeTextFile(String fileName, String content) {
        try (FileWriter writer = new FileWriter(fileName, StandardCharsets.UTF_8)) {
            writer.write(content);
            System.out.println("文本文件写入完成");
        } catch (IOException e) {
            System.err.println("写入文本文件异常: " + e.getMessage());
        }
    }
    
    /**
     * 读取文本文件内容
     */
    public static String readTextFile(String fileName) {
        StringBuilder content = new StringBuilder();
        try (FileReader reader = new FileReader(fileName, StandardCharsets.UTF_8);
             BufferedReader bufferedReader = new BufferedReader(reader)) {
            
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                content.append(line).append("\n");
            }
            
        } catch (IOException e) {
            System.err.println("读取文本文件异常: " + e.getMessage());
        }
        return content.toString();
    }
    
    /**
     * 使用BufferedReader按行读取大文件
     */
    public static void readLargeFileByLines(String fileName) {
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8))) {
            
            String line;
            int lineNumber = 1;
            while ((line = reader.readLine()) != null) {
                System.out.println("第" + lineNumber + "行: " + line);
                lineNumber++;
            }
            
        } catch (IOException e) {
            System.err.println("按行读取文件异常: " + e.getMessage());
        }
    }
    
    /**
     * 使用PrintWriter进行格式化输出
     */
    public static void writeFormattedText(String fileName) {
        try (PrintWriter writer = new PrintWriter(
                new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8))) {
            
            writer.printf("姓名: %s%n", "张三");
            writer.printf("年龄: %d%n", 25);
            writer.printf("薪资: %.2f%n", 8500.50);
            writer.println("这是格式化输出的内容");
            
            System.out.println("格式化文本写入完成");
            
        } catch (IOException e) {
            System.err.println("格式化写入异常: " + e.getMessage());
        }
    }
}

4. 文件管理工具 - FileManager.java

package file;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FileManager {
    
    /**
     * 创建目录
     */
    public static boolean createDirectory(String directoryPath) {
        try {
            Path path = Paths.get(directoryPath);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
                System.out.println("目录创建成功: " + directoryPath);
                return true;
            } else {
                System.out.println("目录已存在: " + directoryPath);
                return false;
            }
        } catch (IOException e) {
            System.err.println("创建目录失败: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * 列出目录下的所有文件
     */
    public static void listFiles(String directoryPath) {
        try (Stream<Path> paths = Files.list(Paths.get(directoryPath))) {
            List<Path> files = paths.collect(Collectors.toList());
            System.out.println("目录 " + directoryPath + " 下的文件:");
            files.forEach(System.out::println);
        } catch (IOException e) {
            System.err.println("列出文件异常: " + e.getMessage());
        }
    }
    
    /**
     * 递归遍历目录树
     */
    public static void walkDirectoryTree(String directoryPath) {
        try {
            Files.walkFileTree(Paths.get(directoryPath), new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    System.out.println("文件: " + file);
                    return FileVisitResult.CONTINUE;
                }
                
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                    System.out.println("目录: " + dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            System.err.println("遍历目录树异常: " + e.getMessage());
        }
    }
    
    /**
     * 获取文件信息
     */
    public static void getFileInfo(String filePath) {
        try {
            Path path = Paths.get(filePath);
            if (Files.exists(path)) {
                BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
                System.out.println("文件名: " + path.getFileName());
                System.out.println("文件大小: " + attrs.size() + " 字节");
                System.out.println("创建时间: " + attrs.creationTime());
                System.out.println("最后修改时间: " + attrs.lastModifiedTime());
                System.out.println("是否为目录: " + attrs.isDirectory());
                System.out.println("是否为文件: " + attrs.isRegularFile());
            } else {
                System.out.println("文件不存在: " + filePath);
            }
        } catch (IOException e) {
            System.err.println("获取文件信息异常: " + e.getMessage());
        }
    }
    
    /**
     * 删除文件或目录
     */
    public static boolean deleteFileOrDirectory(String path) {
        try {
            Path filePath = Paths.get(path);
            if (Files.exists(filePath)) {
                if (Files.isDirectory(filePath)) {
                    // 递归删除目录
                    Files.walkFileTree(filePath, new SimpleFileVisitor<Path>() {
                        @Override
                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                            Files.delete(file);
                            return FileVisitResult.CONTINUE;
                        }
                        
                        @Override
                        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                            Files.delete(dir);
                            return FileVisitResult.CONTINUE;
                        }
                    });
                } else {
                    Files.delete(filePath);
                }
                System.out.println("删除成功: " + path);
                return true;
            } else {
                System.out.println("路径不存在: " + path);
                return false;
            }
        } catch (IOException e) {
            System.err.println("删除失败: " + e.getMessage());
            return false;
        }
    }
}

5. 序列化操作 - SerializationDemo.java

package serialization;

import java.io.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

public class SerializationDemo {
    
    /**
     * 对象序列化到文件
     */
    public static void serializeObject(Object obj, String fileName) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
            oos.writeObject(obj);
            System.out.println("对象序列化完成: " + fileName);
        } catch (IOException e) {
            System.err.println("序列化异常: " + e.getMessage());
        }
    }
    
    /**
     * 从文件反序列化对象
     */
    public static Object deserializeObject(String fileName) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
            Object obj = ois.readObject();
            System.out.println("对象反序列化完成: " + fileName);
            return obj;
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("反序列化异常: " + e.getMessage());
            return null;
        }
    }
    
    /**
     * 序列化员工列表
     */
    public static void serializeEmployeeList(List<Employee> employees, String fileName) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
            oos.writeObject(employees);
            System.out.println("员工列表序列化完成: " + fileName);
        } catch (IOException e) {
            System.err.println("员工列表序列化异常: " + e.getMessage());
        }
    }
    
    /**
     * 反序列化员工列表
     */
    public static List<Employee> deserializeEmployeeList(String fileName) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
            @SuppressWarnings("unchecked")
            List<Employee> employees = (List<Employee>) ois.readObject();
            System.out.println("员工列表反序列化完成: " + fileName);
            return employees;
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("员工列表反序列化异常: " + e.getMessage());
            return new ArrayList<>();
        }
    }
    
    /**
     * 演示序列化过程
     */
    public static void demonstrateSerialization() {
        // 创建员工对象
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee(1L, "张三", "技术部", 15000.0, LocalDate.now()));
        employees.add(new Employee(2L, "李四", "市场部", 12000.0, LocalDate.now().minusDays(30)));
        employees.add(new Employee(3L, "王五", "人事部", 10000.0, LocalDate.now().minusDays(60)));
        
        // 序列化
        String fileName = "employees.ser";
        serializeEmployeeList(employees, fileName);
        
        // 反序列化
        List<Employee> deserializedEmployees = deserializeEmployeeList(fileName);
        
        // 显示结果
        System.out.println("反序列化后的员工列表:");
        deserializedEmployees.forEach(System.out::println);
    }
}

6. 自定义序列化 - CustomSerializationDemo.java

package serialization;

import java.io.*;
import java.time.LocalDate;

public class CustomSerializationDemo implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private transient String password; // 不序列化
    private LocalDate registrationDate;
    
    public CustomSerializationDemo(String name, int age, String password, LocalDate registrationDate) {
        this.name = name;
        this.age = age;
        this.password = password;
        this.registrationDate = registrationDate;
    }
    
    /**
     * 自定义序列化方法
     */
    private void writeObject(ObjectOutputStream oos) throws IOException {
        // 执行默认序列化
        oos.defaultWriteObject();
        
        // 自定义序列化password字段
        oos.writeObject(password != null ? password : "");
        System.out.println("执行自定义序列化");
    }
    
    /**
     * 自定义反序列化方法
     */
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        // 执行默认反序列化
        ois.defaultReadObject();
        
        // 自定义反序列化password字段
        password = (String) ois.readObject();
        if (password.isEmpty()) {
            password = null;
        }
        System.out.println("执行自定义反序列化");
    }
    
    // Getters
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getPassword() { return password; }
    public LocalDate getRegistrationDate() { return registrationDate; }
    
    @Override
    public String toString() {
        return "CustomSerializationDemo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", password='" + (password != null ? "[PROTECTED]" : "null") + '\'' +
                ", registrationDate=" + registrationDate +
                '}';
    }
    
    /**
     * 演示自定义序列化
     */
    public static void demonstrateCustomSerialization() {
        // 创建对象
        CustomSerializationDemo obj = new CustomSerializationDemo(
            "测试用户", 25, "secret123", LocalDate.now());
        
        System.out.println("序列化前的对象:");
        System.out.println(obj);
        
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("custom.ser"))) {
            oos.writeObject(obj);
        } catch (IOException e) {
            System.err.println("序列化异常: " + e.getMessage());
            return;
        }
        
        // 反序列化
        CustomSerializationDemo deserializedObj;
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("custom.ser"))) {
            deserializedObj = (CustomSerializationDemo) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("反序列化异常: " + e.getMessage());
            return;
        }
        
        System.out.println("反序列化后的对象:");
        System.out.println(deserializedObj);
    }
}

7. 文件操作演示 - FileOperationsDemo.java

package file;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.stream.Collectors;

public class FileOperationsDemo {
    
    /**
     * 使用传统IO创建和读取文件
     */
    public static void traditionalFileOperations() {
        String fileName = "traditional.txt";
        
        // 写入文件
        try (FileWriter writer = new FileWriter(fileName)) {
            writer.write("这是传统IO写入的内容\n");
            writer.write("第二行内容\n");
            writer.write("第三行内容\n");
            System.out.println("传统IO写入完成");
        } catch (IOException e) {
            System.err.println("写入文件异常: " + e.getMessage());
        }
        
        // 读取文件
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            System.out.println("传统IO读取内容:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println("读取文件异常: " + e.getMessage());
        }
    }
    
    /**
     * 使用NIO.2创建和读取文件
     */
    public static void nioFileOperations() {
        Path path = Paths.get("nio.txt");
        
        // 写入文件
        try {
            List<String> lines = List.of("这是NIO写入的内容", "第二行内容", "第三行内容");
            Files.write(path, lines);
            System.out.println("NIO写入完成");
        } catch (IOException e) {
            System.err.println("NIO写入异常: " + e.getMessage());
        }
        
        // 读取文件
        try {
            List<String> lines = Files.readAllLines(path);
            System.out.println("NIO读取内容:");
            lines.forEach(System.out::println);
        } catch (IOException e) {
            System.err.println("NIO读取异常: " + e.getMessage());
        }
    }
    
    /**
     * 文件复制操作对比
     */
    public static void compareFileCopyMethods() {
        String sourceFile = "source.txt";
        String targetFile1 = "target1.txt";
        String targetFile2 = "target2.txt";
        
        // 创建源文件
        try {
            Files.write(Paths.get(sourceFile), 
                "这是测试文件内容\n用于测试不同的复制方法\n包含多行数据".getBytes());
        } catch (IOException e) {
            System.err.println("创建源文件异常: " + e.getMessage());
            return;
        }
        
        // 方法1: Files.copy()
        try {
            long startTime = System.currentTimeMillis();
            Files.copy(Paths.get(sourceFile), Paths.get(targetFile1), 
                      StandardCopyOption.REPLACE_EXISTING);
            long endTime = System.currentTimeMillis();
            System.out.println("Files.copy() 耗时: " + (endTime - startTime) + "ms");
        } catch (IOException e) {
            System.err.println("Files.copy() 异常: " + e.getMessage());
        }
        
        // 方法2: 传统流复制
        try {
            long startTime = System.currentTimeMillis();
            try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetFile2))) {
                
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = bis.read(buffer)) != -1) {
                    bos.write(buffer, 0, bytesRead);
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("传统流复制 耗时: " + (endTime - startTime) + "ms");
        } catch (IOException e) {
            System.err.println("传统流复制异常: " + e.getMessage());
        }
    }
    
    /**
     * 监控目录变化
     */
    public static void watchDirectory(String directoryPath) {
        try {
            Path path = Paths.get(directoryPath);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
            }
            
            WatchService watchService = FileSystems.getDefault().newWatchService();
            path.register(watchService, 
                StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY);
            
            System.out.println("开始监控目录: " + directoryPath);
            System.out.println("按Ctrl+C停止监控");
            
            // 简单监控示例(实际应用中应使用单独线程)
            WatchKey key = watchService.poll();
            if (key != null) {
                for (WatchEvent<?> event : key.pollEvents()) {
                    System.out.println("事件类型: " + event.kind() + ", 文件: " + event.context());
                }
                key.reset();
            }
            
        } catch (IOException e) {
            System.err.println("目录监控异常: " + e.getMessage());
        }
    }
}

8. 主应用程序 - MainApplication.java

import file.*;
import io.*;
import serialization.*;
import java.time.LocalDate;
import java.util.Arrays;

public class MainApplication {
    public static void main(String[] args) {
        System.out.println("=== IO流、文件操作、序列化演示程序 ===\n");
        
        // 1. 字节流操作演示
        demonstrateByteStreamOperations();
        
        // 2. 字符流操作演示
        demonstrateCharacterStreamOperations();
        
        // 3. 文件管理操作演示
        demonstrateFileManagement();
        
        // 4. 序列化操作演示
        demonstrateSerializationOperations();
        
        // 5. 自定义序列化演示
        demonstrateCustomSerialization();
        
        // 6. 文件操作对比演示
        demonstrateFileOperations();
    }
    
    private static void demonstrateByteStreamOperations() {
        System.out.println("1. 字节流操作演示");
        System.out.println("-------------------");
        
        // 创建测试文件
        CharacterStreamDemo.writeTextFile("test_input.txt", 
            "这是一个测试文件\n用于演示字节流操作\n包含多行内容");
        
        // 字节流复制
        ByteStreamDemo.copyFileWithByteStream("test_input.txt", "byte_copy.txt");
        
        // 缓冲字节流复制
        ByteStreamDemo.copyFileWithBufferedByteStream("test_input.txt", "buffered_copy.txt");
        
        System.out.println();
    }
    
    private static void demonstrateCharacterStreamOperations() {
        System.out.println("2. 字符流操作演示");
        System.out.println("-------------------");
        
        // 写入文本文件
        CharacterStreamDemo.writeTextFile("character_test.txt", 
            "字符流测试内容\n第二行\n第三行\n第四行内容");
        
        // 读取文本文件
        String content = CharacterStreamDemo.readTextFile("character_test.txt");
        System.out.println("读取的文件内容:");
        System.out.println(content);
        
        // 格式化写入
        CharacterStreamDemo.writeFormattedText("formatted_output.txt");
        
        System.out.println();
    }
    
    private static void demonstrateFileManagement() {
        System.out.println("3. 文件管理操作演示");
        System.out.println("-------------------");
        
        // 创建目录
        FileManager.createDirectory("test_directory");
        FileManager.createDirectory("test_directory/sub_directory");
        
        // 列出文件
        FileManager.listFiles(".");
        
        // 获取文件信息
        FileManager.getFileInfo("character_test.txt");
        
        System.out.println();
    }
    
    private static void demonstrateSerializationOperations() {
        System.out.println("4. 序列化操作演示");
        System.out.println("-------------------");
        
        SerializationDemo.demonstrateSerialization();
        
        System.out.println();
    }
    
    private static void demonstrateCustomSerialization() {
        System.out.println("5. 自定义序列化演示");
        System.out.println("-------------------");
        
        CustomSerializationDemo.demonstrateCustomSerialization();
        
        System.out.println();
    }
    
    private static void demonstrateFileOperations() {
        System.out.println("6. 文件操作对比演示");
        System.out.println("-------------------");
        
        FileOperationsDemo.traditionalFileOperations();
        FileOperationsDemo.nioFileOperations();
        FileOperationsDemo.compareFileCopyMethods();
        
        System.out.println();
    }
}

项目特点

1. 技术覆盖全面

  • 字节流操作FileInputStreamFileOutputStreamBufferedInputStreamBufferedOutputStream
  • 字符流操作FileReaderFileWriterBufferedReaderBufferedWriterPrintWriter
  • 文件管理FilesPathsPathWatchService
  • 序列化ObjectOutputStreamObjectInputStream、自定义序列化

2. 实际应用场景

  • 文件复制和备份
  • 文本文件读写处理
  • 二进制文件操作
  • 对象持久化存储
  • 目录遍历和监控
  • 大文件处理优化

3. 最佳实践展示

  • 资源管理:使用try-with-resources确保资源正确关闭
  • 异常处理:完善的IO异常处理机制
  • 性能优化:缓冲流提高读写效率
  • 编码处理:明确指定字符编码(UTF-8)
  • 类型安全:泛型和类型检查

这个项目涵盖了Java IO操作的各个方面,提供了完整的可运行示例,适合学习和实际项目参考。