Java 流(Stream)、文件(File)和IO详解
在 Java 编程中,流(Stream)、文件(File)和输入 / 输出(IO)是处理外部数据的核心机制。本文将深入解析 Java 中这些概念的底层原理、常用 API 及最佳实践,帮助开发者高效处理文件操作与数据流。
二、文件操作与
1. 文件读写(
2. 带缓冲的字节流(
1. 文件读写(
2. 带缓冲的字符流(
一、Java IO 基础体系
Java IO 基于流的概念,将数据传输抽象为 “流”,分为输入流(读取数据)和输出流(写入数据)。根据处理数据类型,可分为:
- 字节流:处理二进制数据,基类为
InputStream和OutputStream - 字符流:处理文本数据,基类为
Reader和Writer
核心类层次结构:
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 操作,引入
Path、Paths和Files类简化文件操作。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();
}
}
}
八、最佳实践与注意事项
-
资源管理:
- 使用
try-with-resources自动关闭流 - 避免手动调用
close(),减少资源泄漏风险
- 使用
-
性能优化:
- 优先使用带缓冲的流(
BufferedInputStream/BufferedReader) - 批量读写(使用数组作为缓冲区)
- 大文件处理使用 Java 8 Stream API 进行惰性求值
- 优先使用带缓冲的流(
-
字符编码:
- 始终指定字符编码(如
new InputStreamReader(fis, "UTF-8")) - 避免使用平台默认编码,确保跨环境一致性
- 始终指定字符编码(如
-
异常处理:
- 捕获
IOException及其子类 - 区分可恢复异常与不可恢复异常(如文件不存在 vs 磁盘满)
- 捕获
-
安全性:
- 避免路径注入(Path Traversal)漏洞
- 使用
Files.createTempFile()创建临时文件 - 限制文件访问权限(如使用
File.setReadable())
通过掌握 Java IO 的核心机制与 NIO 的高效 API,开发者可以构建健壮、高性能的文件处理系统,应对从简单配置读取到大数据量处理的各类场景。
浙公网安备 33010602011771号