【Java】Java IO 知识体系:从原理到实战,一文吃透流操作
大家好,我是云扬~ 今天来和大家深入聊聊 Java IO 这个基础又核心的知识体系。作为后端开发中数据读写的核心技术,IO 操作贯穿了文件处理、网络通信等诸多场景,但不少小伙伴会被字节流、字符流、各种流实现类搞得晕头转向。这篇文章就从基础概念出发,结合实际代码示例,带大家系统性掌握 Java IO 的核心知识点。
一、初识 Java IO:流是数据传输的桥梁
Java 通过流(Stream) 来处理 IO 操作,流就像一根连接程序与数据源(或目的地)的管道,数据以 “先进先出” 的方式在管道中传输。比如我们读取本地文件时,程序会开启一条通向文件的输入流;写入数据到数据库时,会开启一条通向数据库的输出流。
流有三个核心特性,大家一定要牢记:
- 先进先出:最先写入的数据会最先被读取,就像排队买票一样,顺序不能乱;
- 顺序存取:默认只能按顺序读写数据,无法直接跳转到中间位置(RandomAccessFile 是特例,后面会提到);
- 单向性:每个流要么是输入流(读数据),要么是输出流(写数据),不能同时兼具两种功能。
二、按传输方式划分:字节流 vs 字符流
Java IO 流最核心的分类就是按传输数据的单位,分为字节流和字符流,两者的适用场景和核心类各不相同。
1. 字节流:处理所有二进制数据
字节流以 “字节(byte)” 为单位传输数据,适用于所有文件类型,尤其是图片、音频、视频等二进制文件。核心抽象类是 InputStream(输入字节流)和 OutputStream(输出字节流)。
核心方法示例:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo {
public static void main(String[] args) {
// 字节流读取文件(输入流)
try (FileInputStream fis = new FileInputStream("test.jpg")) {
byte[] buffer = new byte[1024]; // 缓冲区,每次读1024字节
int len;
// read()方法返回读取的字节数,-1表示读取结束
while ((len = fis.read(buffer, 0, buffer.length)) != -1) {
System.out.println("读取到 " + len + " 字节数据");
}
} catch (IOException e) {
e.printStackTrace();
}
// 字节流写入文件(输出流)
try (FileOutputStream fos = new FileOutputStream("test.txt")) {
String content = "这是二进制文件的测试内容";
byte[] data = content.getBytes();
// 从数组0位置开始,写入全部字节
fos.write(data, 0, data.length);
fos.flush(); // 强制刷新缓冲区,确保数据写入
System.out.println("数据写入成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里用到了try-with-resources语法,会自动关闭流,避免手动关闭遗漏导致的资源泄露,这是 Java 7 + 的推荐写法。
2. 字符流:专门处理文本文件
字符流以 “字符(char)” 为单位传输数据,本质是字节流 + 编码转换(如 UTF-8、GBK),适用于文本文件(.txt、.java 等)。核心抽象类是 Reader(输入字符流)和 Writer(输出字符流)。
核心方法示例:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo {
public static void main(String[] args) {
// 字符流读取文本文件
try (FileReader fr = new FileReader("test.txt")) {
char[] buffer = new char[1024];
int len;
// read()返回读取的字符数,-1表示结束
while ((len = fr.read(buffer, 0, buffer.length)) != -1) {
// 将字符数组转为字符串输出
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
// 字符流写入文本文件
try (FileWriter fw = new FileWriter("output.txt")) {
String content = "Java IO 字符流实战\n换行测试";
// 写入字符数组的指定部分
fw.write(content.toCharArray(), 0, content.length());
fw.flush();
System.out.println("\n文本写入成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:字符流处理文本时会自动处理编码问题,而字节流需要手动指定编码(后面转换流会讲到),这是两者的核心区别。
三、按操作对象划分:常用流实现类实战
除了传输方式,IO 流还可以按操作对象(数据源 / 目的地)分类,下面介绍开发中最常用的几种实现类及代码示例。
1. 文件操作:FileInputStream/FileOutputStream、FileReader/FileWriter
这是最基础的文件读写流,前面的示例已经用到,核心是操作本地文件。这里补充一个 “文件复制” 的完整示例(字节流,适用于所有文件):
public class FileCopyDemo {
public static void main(String[] args) {
String sourcePath = "source.pdf"; // 源文件路径
String targetPath = "target.pdf"; // 目标文件路径
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
byte[] buffer = new byte[4096]; // 4KB缓冲区,提升效率
int len;
long startTime = System.currentTimeMillis();
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
long endTime = System.currentTimeMillis();
System.out.println("文件复制完成,耗时:" + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 缓冲流:BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter
缓冲流是对基础流的包装,通过缓冲区减少磁盘 IO 次数,大幅提升读写效率。开发中推荐优先使用缓冲流。
字符缓冲流示例(带换行读取):
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedStreamDemo {
public static void main(String[] args) {
// 缓冲字符流读取(支持按行读取)
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line;
// readLine()方法读取一行数据,返回null表示结束
while ((line = br.readLine()) != null) {
System.out.println("读取到一行:" + line);
}
} catch (IOException e) {
e.printStackTrace();
}
// 缓冲字符流写入(支持换行)
try (BufferedWriter bw = new BufferedWriter(new FileWriter("buffered_output.txt"))) {
bw.write("第一行数据");
bw.newLine(); // 换行操作,跨平台兼容
bw.write("第二行数据");
bw.flush();
System.out.println("缓冲流写入成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 转换流:InputStreamReader/OutputStreamWriter
转换流是字节流和字符流的桥梁,用于处理编码转换。比如用指定编码(如 UTF-8)读取文本文件。
编码转换示例:
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
public class ConvertStreamDemo {
public static void main(String[] args) {
// 字节流 -> 字符流,指定UTF-8编码
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream("utf8_file.txt"), "UTF-8")) {
char[] buffer = new char[1024];
int len;
while ((len = isr.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 对象流:ObjectInputStream/ObjectOutputStream
对象流用于实现对象的序列化与反序列化,即将 Java 对象转换为字节流(序列化),或从字节流恢复为 Java 对象(反序列化)。
对象序列化示例:
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.IOException;
// 序列化的类必须实现Serializable接口(标记接口,无抽象方法)
class User implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号,避免版本冲突
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class ObjectStreamDemo {
public static void main(String[] args) {
// 对象序列化:将User对象写入文件
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.ser"))) {
User user = new User("云扬", 28);
oos.writeObject(user);
System.out.println("对象序列化成功");
} catch (IOException e) {
e.printStackTrace();
}
// 对象反序列化:从文件恢复User对象
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
System.out.println("对象反序列化结果:" + user);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
注意:序列化类必须实现Serializable接口,且类中的所有属性也需支持序列化(基本类型默认支持,引用类型需同样实现接口)。
四、IO 流使用核心注意事项
- 资源关闭:流操作完成后必须关闭,否则会导致资源泄露。推荐使用
try-with-resources语法(Java 7+),自动关闭实现AutoCloseable接口的资源。 - 缓冲区刷新:输出流(尤其是缓冲流)需调用
flush()方法,确保缓冲区数据全部写入目的地,避免数据丢失。 - 编码一致:字符流读写时需保证编码一致(如均使用 UTF-8),否则会出现乱码问题。
- 异常处理:IO 操作可能抛出
IOException,必须进行捕获或声明抛出,避免程序崩溃。
总结
Java IO 知识体系的核心是 “流”,通过字节流处理所有二进制数据,字符流专门处理文本数据,再结合不同操作对象的流实现类,就能满足各种 IO 场景需求。掌握本文中的核心概念、常用类及代码示例,就能轻松应对开发中的 IO 操作问题~
如果大家有疑问或补充,欢迎在评论区留言交流!后续还会分享 Java IO 的进阶知识点(如 NIO),记得关注哦~



