Java 流(Stream)、文件(File)和IO详解

在 Java 编程中,流(Stream)、文件(File)和输入 / 输出(IO)是处理外部数据的核心机制。本文将深入解析 Java 中这些概念的底层原理、常用 API 及最佳实践,帮助开发者高效处理文件操作与数据流。

一、Java IO 基础体系

Java IO 基于的概念,将数据传输抽象为 “流”,分为输入流(读取数据)和输出流(写入数据)。根据处理数据类型,可分为:

  1. 字节流:处理二进制数据,基类为InputStreamOutputStream
  2. 字符流:处理文本数据,基类为ReaderWriter

核心类层次结构:

InputStream / OutputStream
├── FileInputStream / FileOutputStream
├── BufferedInputStream / BufferedOutputStream
├── DataInputStream / DataOutputStream
└── ObjectInputStream / ObjectOutputStream

Reader / Writer
├── FileReader / FileWriter
├── BufferedReader / BufferedWriter
└── InputStreamReader / OutputStreamWriter
 

二、文件操作与File

java.io.File类用于表示文件或目录的抽象路径,提供文件元数据操作(创建、删除、重命名等),但不直接处理文件内容。

常用方法示例:

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

public class FileExample {
    public static void main(String[] args) {
        // 创建File对象
        File file = new File("example.txt");
        
        try {
            // 创建新文件
            if (file.createNewFile()) {
                System.out.println("文件创建成功");
            }
            
            // 文件信息查询
            System.out.println("文件是否存在: " + file.exists());
            System.out.println("文件大小: " + file.length() + " 字节");
            System.out.println("是否为目录: " + file.isDirectory());
            
            // 目录操作
            File dir = new File("mydir");
            if (dir.mkdir()) {
                System.out.println("目录创建成功");
            }
            
            // 列出目录内容
            File[] files = dir.listFiles();
            if (files != null) {
                for (File f : files) {
                    System.out.println(f.getName());
                }
            }
            
            // 文件重命名
            File newFile = new File("new_example.txt");
            if (file.renameTo(newFile)) {
                System.out.println("文件重命名成功");
            }
            
            // 删除文件
            if (newFile.delete()) {
                System.out.println("文件删除成功");
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

三、字节流操作

1. 文件读写(FileInputStream/FileOutputStream

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

public class ByteStreamExample {
    public static void main(String[] args) {
        try (
            // 自动关闭资源的try-with-resources语句
            FileInputStream fis = new FileInputStream("input.txt");
            FileOutputStream fos = new FileOutputStream("output.txt")
        ) {
            int byteRead;
            // 每次读取一个字节,返回-1表示文件末尾
            while ((byteRead = fis.read()) != -1) {
                fos.write(byteRead);
            }
            System.out.println("文件复制完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

2. 带缓冲的字节流(BufferedInputStream/BufferedOutputStream

通过缓冲区减少系统 IO 调用,提升性能:
import java.io.*;

public class BufferedByteStreamExample {
    public static void main(String[] args) {
        try (
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))
        ) {
            byte[] buffer = new byte[8192];  // 8KB缓冲区
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

四、字符流操作

1. 文件读写(FileReader/FileWriter

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

public class CharacterStreamExample {
    public static void main(String[] args) {
        try (
            FileReader reader = new FileReader("input.txt");
            FileWriter writer = new FileWriter("output.txt")
        ) {
            int charRead;
            while ((charRead = reader.read()) != -1) {
                writer.write(charRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

2. 带缓冲的字符流(BufferedReader/BufferedWriter

import java.io.*;

public class BufferedCharacterStreamExample {
    public static void main(String[] args) {
        try (
            BufferedReader br = new BufferedReader(new FileReader("input.txt"));
            BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))
        ) {
            String line;
            // 逐行读取
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();  // 写入换行符
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

五、Java NIO 与 Path API(Java 7+)

Java NIO(New IO)提供更高效的非阻塞 IO 操作,引入PathPathsFiles类简化文件操作。

1. Path 与 Files 类基础

import java.nio.file.*;

public class NIOExample {
    public static void main(String[] args) {
        try {
            // 创建Path对象
            Path path = Paths.get("example.txt");
            
            // 读取文件内容(一次性读取所有行)
            byte[] bytes = Files.readAllBytes(path);
            String content = new String(bytes);
            
            // 写入文件
            String data = "Hello, NIO!";
            Files.write(path, data.getBytes());
            
            // 文件复制
            Path source = Paths.get("source.txt");
            Path target = Paths.get("target.txt");
            Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
            
            // 目录操作
            Path dir = Paths.get("newdir");
            Files.createDirectory(dir);
            
            // 遍历目录
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
                for (Path entry : stream) {
                    System.out.println(entry.getFileName());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

2. 异步文件操作(Java 7+)

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

public class AsyncFileExample {
    public static void main(String[] args) {
        Path path = Paths.get("large_file.txt");
        
        // 异步读取文件
        CompletableFuture.runAsync(() -> {
            try {
                byte[] data = Files.readAllBytes(path);
                System.out.println("文件读取完成,大小: " + data.length + " 字节");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        
        // 主线程继续执行其他任务
        System.out.println("主线程继续执行...");
    }
}
 

六、Java 8 Stream API 与文件处理

Java 8 引入的 Stream API 简化了集合与 IO 操作,结合Files.lines()可高效处理大文件。

1. 逐行处理大文件

 
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class StreamFileExample {
    public static void main(String[] args) {
        Path path = Paths.get("large_file.txt");
        
        try (var lines = Files.lines(path)) {  // 自动关闭流
            lines
                .filter(line -> line.contains("keyword"))  // 过滤包含关键字的行
                .limit(10)  // 取前10条
                .forEach(System.out::println);  // 打印结果
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

2. 并行处理文件内容

 
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ParallelStreamExample {
    public static void main(String[] args) {
        Path path = Paths.get("large_file.txt");
        
        try (var lines = Files.lines(path)) {
            lines
                .parallel()  // 转换为并行流
                .filter(line -> line.length() > 100)  // 过滤长文本行
                .map(String::toUpperCase)  // 转换为大写
                .forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

七、序列化与对象 IO

Java 提供对象序列化机制,允许将对象转换为字节流存储或传输。

1. 序列化对象

 
import java.io.*;

// 实现Serializable接口
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Getters and setters
    public String getName() { return name; }
    public int getAge() { return age; }
}

public class SerializationExample {
    public static void main(String[] args) {
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("person.ser"))) {
            Person person = new Person("Alice", 30);
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("person.ser"))) {
            Person restoredPerson = (Person) ois.readObject();
            System.out.println("Name: " + restoredPerson.getName());
            System.out.println("Age: " + restoredPerson.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
 

八、最佳实践与注意事项

  1. 资源管理
    • 使用try-with-resources自动关闭流
    • 避免手动调用close(),减少资源泄漏风险
  2. 性能优化
    • 优先使用带缓冲的流(BufferedInputStream/BufferedReader
    • 批量读写(使用数组作为缓冲区)
    • 大文件处理使用 Java 8 Stream API 进行惰性求值
  3. 字符编码
    • 始终指定字符编码(如new InputStreamReader(fis, "UTF-8")
    • 避免使用平台默认编码,确保跨环境一致性
  4. 异常处理
    • 捕获IOException及其子类
    • 区分可恢复异常与不可恢复异常(如文件不存在 vs 磁盘满)
  5. 安全性
    • 避免路径注入(Path Traversal)漏洞
    • 使用Files.createTempFile()创建临时文件
    • 限制文件访问权限(如使用File.setReadable()

通过掌握 Java IO 的核心机制与 NIO 的高效 API,开发者可以构建健壮、高性能的文件处理系统,应对从简单配置读取到大数据量处理的各类场景。

posted on 2025-07-16 10:13  coding博客  阅读(246)  评论(0)    收藏  举报