开篇概览:Java I/O 体系结构
Java 的输入输出(I/O)系统基于流(Stream) 模型,所有数据读写都通过流进行。java.io 包提供了丰富的类库,分为两大体系:
- 文件操作基础:
File类 —— 用于管理文件/目录元数据(路径、属性、创建/删除等),不涉及内容读写; - 流式数据传输:
- 字节流(Byte Streams):处理原始二进制数据(如图片、音频、视频),以
InputStream/OutputStream为基类; - 字符流(Character Streams):专用于文本数据(自动处理字符编码),以
Reader/Writer为基类; - 桥梁流(Bridge Streams):连接字节流与字符流(如
InputStreamReader); - 缓冲流(Buffered Streams):提升 I/O 效率。
- 字节流(Byte Streams):处理原始二进制数据(如图片、音频、视频),以
⚠️ 重要前提:
File不能读写文件内容,仅操作文件系统元信息;- 所有流操作后必须关闭资源,推荐使用
try-with-resources自动管理。
一、File 类:文件与目录的抽象表示
1.1 核心功能
- 表示文件或目录的路径(绝对/相对);
- 创建/删除/重命名文件或目录;
- 查询文件属性(大小、权限、是否存在等);
- 不涉及文件内容读写。
1.2 常用构造方法与方法
| 方法 | 说明 |
|---|---|
File(String pathname) | 通过路径字符串创建 File 对象 |
boolean exists() | 判断文件/目录是否存在 |
boolean isFile() / isDirectory() | 判断是否为文件/目录 |
long length() | 获取文件大小(字节) |
boolean createNewFile() | 创建新文件(若不存在) |
boolean mkdir() / mkdirs() | 创建目录(mkdirs() 可创建多级目录) |
boolean delete() | 删除文件或空目录 |
String[] list() / File[] listFiles() | 列出目录内容 |
1.3 示例:File 类使用(详细中文注释)
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) {
// 1. 创建 File 对象(表示一个文件路径)
String filePath = "example.txt";
File file = new File(filePath);
// 2. 检查文件是否存在
if (!file.exists()) {
try {
// 创建新文件(返回 true 表示创建成功)
boolean created = file.createNewFile();
System.out.println("文件创建成功: " + created);
} catch (IOException e) {
System.err.println("创建文件时发生错误: " + e.getMessage());
}
} else {
System.out.println("文件已存在");
}
// 3. 获取文件基本信息
System.out.println("文件名: " + file.getName());
System.out.println("绝对路径: " + file.getAbsolutePath());
System.out.println("是否为文件: " + file.isFile());
System.out.println("文件大小(字节): " + file.length());
// 4. 创建目录示例
File dir = new File("testDir/subDir");
if (dir.mkdirs()) { // mkdirs() 可创建多级目录
System.out.println("目录创建成功: " + dir.getAbsolutePath());
}
// 5. 列出目录内容
File parentDir = new File("."); // 当前目录
if (parentDir.isDirectory()) {
String[] files = parentDir.list(); // 获取文件名数组
System.out.println("当前目录下的文件/目录:");
for (String name : files) {
System.out.println(" - " + name);
}
}
// 6. 删除文件(程序结束前删除示例文件)
// 注意:delete() 不能删除非空目录
if (file.exists()) {
boolean deleted = file.delete();
System.out.println("文件删除成功: " + deleted);
}
}
}
✅ 关键点:
File是平台无关的路径表示;- 路径分隔符使用
/或File.separator(避免硬编码\);delete()无法删除非空目录(需先清空内容)。
二、字节流(Byte Streams):处理二进制数据
2.1 体系结构
InputStream(抽象基类)
├── FileInputStream // 从文件读取字节
├── BufferedInputStream // 带缓冲的输入流(提升效率)
└── ...(其他子类)
OutputStream(抽象基类)
├── FileOutputStream // 向文件写入字节
├── BufferedOutputStream // 带缓冲的输出流
└── ...(其他子类)
2.2 核心特点
- 以 8 位字节(byte) 为单位读写;
- 适用于所有类型数据(文本、图片、视频等);
- 不处理字符编码。
2.3 示例:FileInputStream / FileOutputStream(复制图片)
import java.io.*;
public class ByteStreamDemo {
public static void main(String[] args) {
// 源文件(假设项目根目录有 input.jpg)
String sourcePath = "input.jpg";
// 目标文件
String targetPath = "output.jpg";
// 使用 try-with-resources 自动关闭流
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
byte[] buffer = new byte[1024]; // 缓冲区(1KB)
int bytesRead;
// 循环读取,直到返回 -1(表示文件结束)
while ((bytesRead = fis.read(buffer)) != -1) {
// 将读取的字节写入目标文件
fos.write(buffer, 0, bytesRead);
}
System.out.println("图片复制成功!");
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("I/O 错误: " + e.getMessage());
}
}
}
✅ 优化建议:
- 使用缓冲区(如
byte[1024])避免频繁系统调用;- 永远不要用单字节读写(
read()/write(int)),效率极低。
2.4 示例:BufferedInputStream / BufferedOutputStream(带缓冲的字节流)
import java.io.*;
public class BufferedByteStreamDemo {
public static void main(String[] args) {
try (// 包装 FileInputStream 为 BufferedInputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("data.bin"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.bin"))) {
int data;
// 逐字节读取(因有缓冲,实际效率高)
while ((data = bis.read()) != -1) {
bos.write(data);
}
// 缓冲流会在 close() 时自动 flush()
System.out.println("二进制文件复制完成(使用缓冲流)");
} catch (IOException e) {
e.printStackTrace();
}
}
}
✅ 缓冲流优势:
- 内部维护缓冲区,减少底层 I/O 调用次数;
BufferedOutputStream在close()或flush()时强制写入数据。
三、字符流(Character Streams):处理文本数据
3.1 体系结构
Reader(抽象基类)
├── FileReader // 从文件读取字符(使用默认编码)
├── BufferedReader // 带缓冲的字符输入流
├── InputStreamReader // 将字节流转换为字符流(可指定编码)
└── ...
Writer(抽象基类)
├── FileWriter // 向文件写入字符(使用默认编码)
├── BufferedWriter // 带缓冲的字符输出流
├── OutputStreamWriter // 将字符流转换为字节流(可指定编码)
└── ...
3.2 核心特点
- 以 16 位字符(char) 为单位读写;
- 自动处理字符编码(如 UTF-8、GBK);
- 仅适用于文本数据。
3.3 示例:FileReader / FileWriter(读写文本文件)
import java.io.*;
public class CharStreamDemo {
public static void main(String[] args) {
String textFile = "message.txt";
// 1. 写入文本
try (FileWriter writer = new FileWriter(textFile)) {
writer.write("Hello, Java 字符流!\n");
writer.write("这是第二行文本。");
// FileWriter 会自动使用系统默认编码(如 Windows: GBK, Linux/macOS: UTF-8)
System.out.println("文本写入完成");
} catch (IOException e) {
e.printStackTrace();
}
// 2. 读取文本
try (FileReader reader = new FileReader(textFile)) {
int ch;
System.out.println("读取内容:");
while ((ch = reader.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
⚠️ 问题:
FileReader/FileWriter无法指定编码,易导致乱码!
3.4 示例:InputStreamReader / OutputStreamWriter(指定编码)
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BridgeStreamDemo {
public static void main(String[] args) {
String textFile = "utf8_message.txt";
// 1. 使用 UTF-8 编码写入
try (FileOutputStream fos = new FileOutputStream(textFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
osw.write("中文内容:Java I/O 支持 UTF-8 编码!\n");
osw.write("English: UTF-8 is safe for multilingual text.");
System.out.println("UTF-8 文本写入完成");
} catch (IOException e) {
e.printStackTrace();
}
// 2. 使用 UTF-8 编码读取
try (FileInputStream fis = new FileInputStream(textFile);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
char[] buffer = new char[100];
int charsRead;
System.out.println("UTF-8 读取内容:");
while ((charsRead = isr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, charsRead));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
✅ 最佳实践:
- 永远显式指定编码(推荐
StandardCharsets.UTF_8);- 避免依赖系统默认编码(跨平台易乱码)。
3.5 示例:BufferedReader / BufferedWriter(高效文本处理)
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BufferedCharStreamDemo {
public static void main(String[] args) {
String logFile = "app.log";
// 1. 写入多行日志(使用 BufferedWriter)
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(logFile), StandardCharsets.UTF_8))) {
writer.write("INFO: 应用启动");
writer.newLine(); // 写入系统换行符(\n 或 \r\n)
writer.write("DEBUG: 初始化配置完成");
writer.newLine();
writer.write("ERROR: 数据库连接失败");
// BufferedWriter 会在 close() 时自动 flush()
System.out.println("日志写入完成");
} catch (IOException e) {
e.printStackTrace();
}
// 2. 按行读取日志(使用 BufferedReader)
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(logFile), StandardCharsets.UTF_8))) {
String line;
System.out.println("日志内容:");
while ((line = reader.readLine()) != null) { // readLine() 自动处理换行符
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
✅ BufferedReader 优势:
readLine()方法简化文本行读取;- 内部缓冲提升性能;
- 自动处理不同平台的换行符(
\n,\r\n)。
四、I/O 流选择指南
| 需求 | 推荐流 |
|---|---|
| 读写二进制文件(图片、视频) | FileInputStream / FileOutputStream + Buffered 包装 |
| 读写文本文件(指定编码) | InputStreamReader / OutputStreamWriter + Buffered 包装 |
| 按行处理文本 | BufferedReader(readLine()) |
| 高效写入多行文本 | BufferedWriter(write() + newLine()) |
| 避免乱码 | 永远显式指定编码(如 StandardCharsets.UTF_8) |
五、关键注意事项
5.1 资源管理
- 必须关闭流!否则可能导致:
- 文件句柄泄漏;
- 缓冲数据未写入(
BufferedOutputStream);
- 推荐使用
try-with-resources(Java 7+):try (InputStream is = new FileInputStream("file.txt")) { // 使用流 } // 自动调用 is.close()
5.2 编码问题
FileReader/FileWriter使用系统默认编码,跨平台易乱码;- 始终使用
InputStreamReader/OutputStreamWriter并指定编码。
5.3 性能优化
- 避免单字节/字符读写;
- 使用缓冲流(
BufferedInputStream/BufferedReader); - 缓冲区大小通常设为 8KB~64KB(如
new byte[8192])。
六、总结
| 组件 | 作用 | 关键类 |
|---|---|---|
| File | 文件/目录元操作 | File |
| 字节流 | 二进制数据读写 | FileInputStream, BufferedOutputStream |
| 字符流 | 文本数据读写 | FileReader, BufferedWriter |
| 桥梁流 | 字节流 ↔ 字符流 | InputStreamReader, OutputStreamWriter |
📌 核心原则:
“文本用字符流(指定编码),二进制用字节流,永远用缓冲,资源自动关。”
掌握 Java I/O 基础,是处理文件、网络、数据库等数据传输场景的必备能力。
1487

被折叠的 条评论
为什么被折叠?



