1. 引言

在Java编程中,文件操作和输入输出(IO)是日常开发中不可或缺的部分。无论是读取配置文件、处理日志文件,还是实现数据持久化,都需要用到Java的File类和IO流。本文将深入讲解Java中File类的基本用法、IO流的分类体系,并通过丰富的代码示例帮助您掌握这些核心概念。

2. File类:文件与目录的操作

2.1 File类概述

java.io.File类是Java中用于表示文件和目录路径名的抽象表示。它提供了创建、删除、重命名文件和目录,以及查询文件属性等功能。

import java.io.File;

public class FileBasicExample {
    public static void main(String[] args) {
        // 创建File对象(不实际创建文件)
        File file = new File("test.txt");
        System.out.println("文件路径: " + file.getPath());
        System.out.println("绝对路径: " + file.getAbsolutePath());
        System.out.println("文件名: " + file.getName());
        
        // 检查文件是否存在
        if (file.exists()) {
            System.out.println("文件存在");
            System.out.println("文件大小: " + file.length() + " bytes");
            System.out.println("是否可读: " + file.canRead());
            System.out.println("是否可写: " + file.canWrite());
        } else {
            System.out.println("文件不存在");
        }
    }
}

2.2 文件与目录操作

import java.io.File;
import java.io.IOException;

public class FileOperations {
    public static void main(String[] args) {
        // 创建文件
        File newFile = new File("newfile.txt");
        try {
            if (newFile.createNewFile()) {
                System.out.println("文件创建成功: " + newFile.getName());
            } else {
                System.out.println("文件已存在");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 创建目录
        File dir = new File("mydir");
        if (dir.mkdir()) {
            System.out.println("目录创建成功");
        }
        
        // 列出目录内容
        File currentDir = new File(".");
        System.out.println("\n当前目录内容:");
        String[] files = currentDir.list();
        if (files != null) {
            for (String fileName : files) {
                System.out.println(fileName);
            }
        }
        
        // 删除文件
        if (newFile.delete()) {
            System.out.println("\n文件删除成功");
        }
    }
}

3. IO流体系结构

3.1 流的分类

Java IO流按照不同的维度可以分为:

  1. 按数据流向

    • 输入流(InputStream/Reader):从数据源读取数据
    • 输出流(OutputStream/Writer):向目标写入数据
  2. 按处理单位

    • 字节流:以字节为单位(8位),适合处理所有类型文件
    • 字符流:以字符为单位(16位),适合处理文本文件
  3. 按功能

    • 节点流:直接从数据源读写数据
    • 处理流:对节点流进行包装,提供增强功能

3.2 字节流(Byte Streams)

字节流用于处理二进制数据,如图片、音频、视频等。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    
    // 使用FileInputStream读取文件
    public static void readFile(String filePath) {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            int byteData;
            System.out.println("文件内容(字节形式):");
            while ((byteData = fis.read()) != -1) {
                System.out.print((char) byteData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 使用FileOutputStream写入文件
    public static void writeFile(String filePath, String content) {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            byte[] bytes = content.getBytes();
            fos.write(bytes);
            System.out.println("文件写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 文件复制(字节流方式)
    public static void copyFile(String source, String destination) {
        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(destination)) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
            System.out.println("文件复制完成");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        // 写入文件
        writeFile("test.txt", "Hello, Java IO Stream!");
        
        // 读取文件
        readFile("test.txt");
        
        // 复制文件
        copyFile("test.txt", "test_copy.txt");
    }
}

3.3 字符流(Character Streams)

字符流专门用于处理文本文件,支持字符编码。

import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;

public class CharacterStreamExample {
    
    // 使用FileReader和FileWriter
    public static void basicReadWrite() {
        // 写入文件
        try (FileWriter writer = new FileWriter("textfile.txt")) {
            writer.write("Java字符流示例\n");
            writer.write("第二行内容\n");
            writer.write("第三行内容");
            System.out.println("文件写入完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 读取文件
        try (FileReader reader = new FileReader("textfile.txt")) {
            int charData;
            System.out.println("\n文件内容:");
            while ((charData = reader.read()) != -1) {
                System.out.print((char) charData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 使用缓冲流提高效率
    public static void bufferedStreamExample() {
        // 使用BufferedWriter写入
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("buffered.txt"))) {
            bw.write("使用缓冲流写入数据");
            bw.newLine();
            bw.write("这是第二行");
            bw.newLine();
            bw.write("性能更好,效率更高");
            System.out.println("缓冲写入完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 使用BufferedReader读取
        try (BufferedReader br = new BufferedReader(new FileReader("buffered.txt"))) {
            String line;
            System.out.println("\n使用缓冲流读取:");
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        basicReadWrite();
        bufferedStreamExample();
    }
}

4. 高级IO操作

4.1 对象序列化

Java提供了对象序列化机制,可以将对象转换为字节序列进行存储或传输。

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

// 可序列化的类
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private Date birthDate;
    
    public Person(String name, int age, Date birthDate) {
        this.name = name;
        this.age = age;
        this.birthDate = birthDate;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + 
               ", birthDate=" + birthDate + "}";
    }
}

public class ObjectStreamExample {
    
    // 序列化对象到文件
    public static void serializeObject(Person person, String filePath) {
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(filePath))) {
            oos.writeObject(person);
            System.out.println("对象序列化完成: " + person);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 从文件反序列化对象
    public static Person deserializeObject(String filePath) {
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(filePath))) {
            Person person = (Person) ois.readObject();
            System.out.println("对象反序列化完成: " + person);
            return person;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    public static void main(String[] args) {
        Person person = new Person("张三", 25, new Date());
        
        // 序列化
        serializeObject(person, "person.ser");
        
        // 反序列化
        Person restoredPerson = deserializeObject("person.ser");
    }
}

4.2 随机访问文件

RandomAccessFile类允许在文件的任意位置进行读写操作。

import java.io.RandomAccessFile;
import java.io.IOException;

public class RandomAccessFileExample {
    
    public static void main(String[] args) {
        String filePath = "random.dat";
        
        try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
            // 写入数据
            raf.writeUTF("Java");  // 写入字符串
            raf.writeInt(100);     // 写入整数
            raf.writeDouble(3.14); // 写入双精度浮点数
            
            // 移动到文件开头重新读取
            raf.seek(0);
            
            // 读取数据
            String str = raf.readUTF();
            int num = raf.readInt();
            double d = raf.readDouble();
            
            System.out.println("读取的数据:");
            System.out.println("字符串: " + str);
            System.out.println("整数: " + num);
            System.out.println("浮点数: " + d);
            
            // 在文件末尾追加数据
            raf.seek(raf.length());
            raf.writeUTF("追加的内容");
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. NIO(New IO)简介

Java NIO提供了更高效的IO操作方式,主要包含三个核心组件:Channel、Buffer和Selector。

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

public class NIOExample {
    
    // 使用Files类简化文件操作
    public static void filesExample() throws IOException {
        // 写入文件
        String content = "Java NIO Files类示例\n第二行内容";
        Path path = Paths.get("nio_file.txt");
        
        Files.write(path, content.getBytes(StandardCharsets.UTF_8));
        System.out.println("文件写入完成");
        
        // 读取文件
        List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
        System.out.println("\n文件内容:");
        for (String line : lines) {
            System.out.println(line);
        }
        
        // 复制文件
        Path copyPath = Paths.get("nio_file_copy.txt");
        Files.copy(path, copyPath, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("文件复制完成");
    }
    
    // 使用Path接口
    public static void pathExample() {
        Path path = Paths.get("src", "main", "java", "Test.java");
        
        System.out.println("Path信息:");
        System.out.println("文件名: " + path.getFileName());
        System.out.println("父目录: " + path.getParent());
        System.out.println("根目录: " + path.getRoot());
        System.out.println("绝对路径: " + path.toAbsolutePath());
        System.out.println("是否是绝对路径: " + path.isAbsolute());
        
        // 路径操作
        Path resolved = path.resolve("config.properties");
        System.out.println("解析后的路径: " + resolved);
    }
    
    public static void main(String[] args) {
        try {
            filesExample();
            pathExample();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6. 最佳实践与常见问题

6.1 资源管理

正确做法(使用try-with-resources):

try (FileInputStream fis = new FileInputStream("file.txt");
     FileOutputStream fos = new FileOutputStream("output.txt")) {
    // 使用流
    int data;
    while ((data = fis.read()) != -1) {
        fos.write(data);
    }
} catch (IOException e) {
    e.printStackTrace();
}
// 自动关闭资源,无需手动调用close()

6.2 性能优化建议

  1. 使用缓冲流:对于频繁的IO操作,使用BufferedInputStream/BufferedOutputStream
  2. 选择合适的缓冲区大小:通常8KB-64KB效果较好
  3. 批量读写:避免单字节读写,使用byte数组批量操作
  4. 及时关闭资源:防止资源泄漏

6.3 常见异常处理

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class ExceptionHandlingExample {
    
    public static void safeFileRead(String filePath) {
        File file = new File(filePath);
        
        if (!file.exists()) {
            System.out.println("文件不存在: " + filePath);
            return;
        }
        
        if (!file.canRead()) {
            System.out.println("文件不可读: " + filePath);
            return;
        }
        
        try (Scanner scanner = new Scanner(file)) {
            while (scanner.hasNextLine()) {
                System.out.println(scanner.nextLine());
            }
        } catch (FileNotFoundException e) {
            // 虽然已经检查过,但多线程环境下仍需处理
            System.out.println("文件读取异常: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        safeFileRead("nonexistent.txt");
        safeFileRead("test.txt");
    }
}

7. 实战案例:文件加密工具

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

public class FileEncryptor {
    
    // 简单的XOR加密
    private static byte[] xorEncrypt(byte[] data, byte key) {
        byte[] encrypted = new byte[data.length];
        for (int i = 0; i < data.length; i++) {
            encrypted[i] = (byte) (data[i] ^ key);
        }
        return encrypted;
    }
    
    // 加密文件
    public static void encryptFile(String sourcePath, String destPath, byte key) {
        try (FileInputStream fis = new FileInputStream(sourcePath);
             FileOutputStream fos = new FileOutputStream(destPath)) {
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            
            while ((bytesRead = fis.read(buffer)) != -1) {
                byte[] encrypted = xorEncrypt(buffer, key);
                fos.write(encrypted, 0, bytesRead);
            }
            
            System.out.println("文件加密完成: " + destPath);
            
        } catch (IOException e) {
            System.err.println("加密失败: " + e.getMessage());
        }
    }
    
    // 解密文件(XOR加密的解密就是再次加密)
    public static void decryptFile(String sourcePath, String destPath, byte key) {
        encryptFile(sourcePath, destPath, key); // XOR特性:加密=解密
        System.out.println("文件解密完成: " + destPath);
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("文件加密工具");
        System.out.print("请输入源文件路径: ");
        String source = scanner.nextLine();
        
        System.out.print("请输入目标文件路径: ");
        String dest = scanner.nextLine();
        
        System.out.print("请输入加密密钥(0-255): ");
        byte key = (byte) scanner.nextInt();
        
        System.out.print("请选择操作(1-加密, 2-解密): ");
        int choice = scanner.nextInt();
        
        if (choice == 1) {
            encryptFile(source, dest, key);
        } else if (choice == 2) {
            decryptFile(source, dest, key);
        } else {
            System.out.println("无效选择");
        }
        
        scanner.close();
    }
}

8. 总结

Java的File类和IO流提供了强大的文件操作能力:

  1. File类:用于文件和目录的基本操作,如创建、删除、查询属性等
  2. 字节流:适合处理所有类型文件,特别是二进制文件
  3. 字符流:专门为文本文件设计,支持字符编码
  4. 缓冲流:提高IO性能,减少系统调用次数
  5. 对象流:实现对象的序列化和反序列化
  6. NIO:提供更高效的IO操作方式

在实际开发中,应根据具体需求选择合适的IO类,并注意资源管理和异常处理。对于大量数据的IO操作,建议使用缓冲流和合适的缓冲区大小来优化性能。

掌握这些基础知识后,您可以更高效地处理Java中的文件操作和IO任务,为开发更复杂的应用程序打下坚实基础。

更多推荐