Java IO流完全指南
摘要
Java IO流是输入输出操作的核心技术,包括字节流和字符流两大类。通过InputStream、OutputStream、Reader、Writer四大抽象类及其子类,实现文件读写、缓冲处理、对象序列化等功能。
目录
IO流概述
IO流分类
按流的方向分类:
- 输入流(Input):从数据源读取数据到程序
- 输出流(Output):从程序写入数据到目标
按数据单位分类:
- 字节流(Byte):以字节为单位处理数据,适用于所有文件类型
- 字符流(Character):以字符为单位处理数据,适用于文本文件
IO流体系结构
markdown
IO流体系
├── 字节流
│ ├── InputStream(输入)
│ └── OutputStream(输出)
└── 字符流
├── Reader(输入)
└── Writer(输出)
IO四大抽象类
1. InputStream(字节输入流)
所有字节输入流的抽象超类。
构造方法
java
InputStream()
主要方法
| 方法 | 返回类型 | 描述 |
|---|---|---|
available() |
int | 返回可读取的字节数 |
close() |
void | 关闭流,释放资源 |
mark(int readlimit) |
void | 在流中标记当前位置 |
read() |
int | 读取下一个字节,返回-1表示结束 |
read(byte[] b) |
int | 读取字节到数组中 |
read(byte[] b, int off, int len) |
int | 读取指定长度的字节 |
reset() |
void | 重置到标记位置 |
2. OutputStream(字节输出流)
所有字节输出流的抽象超类。
构造方法
java
OutputStream()
主要方法
| 方法 | 返回类型 | 描述 |
|---|---|---|
close() |
void | 关闭流 |
flush() |
void | 刷新并强制写出缓冲的字节 |
write(byte[] b) |
void | 写入字节数组 |
write(byte[] b, int off, int len) |
void | 写入字节数组的指定部分 |
write(int b) |
void | 写入指定字节 |
3. Reader(字符输入流)
读取字符流的抽象类。
构造方法
java
Reader()
Reader(Object lock) // 指定同步锁
主要方法
| 方法 | 返回类型 | 描述 |
|---|---|---|
close() |
void | 关闭流 |
read() |
int | 读取单个字符 |
read(char[] cbuf) |
int | 读取字符到数组 |
read(char[] cbuf, int off, int len) |
int | 读取字符到数组指定位置 |
ready() |
boolean | 判断是否准备好读取 |
skip(long n) |
long | 跳过指定数量的字符 |
4. Writer(字符输出流)
写入字符流的抽象类。
构造方法
java
Writer()
Writer(Object lock) // 指定同步锁
主要方法
| 方法 | 返回类型 | 描述 |
|---|---|---|
close() |
void | 关闭流 |
flush() |
void | 刷新流 |
write(char[] cbuf) |
void | 写入字符数组 |
write(int c) |
void | 写入单个字符 |
write(String str) |
void | 写入字符串 |
文件读写操作
字符文件操作
1. FileReader - 字符文件读取
java
// 构造方法
FileReader(File file)
FileReader(String fileName)
2. FileWriter - 字符文件写入
java
// 构造方法
FileWriter(String fileName) // 覆盖写入
FileWriter(String fileName, boolean append) // append=true表示追加
字符文件复制示例
java
import java.io.*;
public class CopyText {
public static void main(String[] args) throws IOException {
FileReader fr = null;
FileWriter fw = null;
try {
// 创建读取流与源文件关联
fr = new FileReader("source.txt");
// 创建写入流与目标文件关联
fw = new FileWriter("target.txt");
int ch;
// 循环读取字符并写入
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
} finally {
// 关闭流资源
if (fw != null) fw.close();
if (fr != null) fr.close();
}
}
}
字节文件操作
1. FileInputStream - 字节文件读取
java
// 构造方法
FileInputStream(File file)
FileInputStream(String name)
// 主要方法
available() // 返回可读字节数
read() // 读取单个字节
read(byte[] b) // 读取字节到数组
2. FileOutputStream - 字节文件写入
java
// 构造方法
FileOutputStream(File file)
FileOutputStream(String name)
// 主要方法
write(byte[] b) // 写入字节数组
write(byte[] b, int off, int len) // 写入字节数组的部分
write(int b) // 写入单个字节
字节文件复制示例(图片复制)
java
import java.io.*;
public class CopyImage {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("source.jpg");
fos = new FileOutputStream("target.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException("文件复制失败", e);
} finally {
try {
if (fis != null) fis.close();
if (fos != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
缓冲流通过内置缓冲区提高IO操作效率,减少对底层系统的访问次数。
字符缓冲流
1. BufferedReader
java
// 构造方法
BufferedReader(Reader in)
BufferedReader(Reader in, int sz) // 指定缓冲区大小
// 特有方法
readLine() // 读取一行文本
2. BufferedWriter
java
// 构造方法
BufferedWriter(Writer out)
BufferedWriter(Writer out, int sz)
// 特有方法
newLine() // 写入行分隔符
字符缓冲流示例
java
import java.io.*;
public class BufferedCopyText {
public static void main(String[] args) {
BufferedReader bufr = null;
BufferedWriter bufw = null;
try {
bufr = new BufferedReader(new FileReader("source.txt"));
bufw = new BufferedWriter(new FileWriter("target.txt"));
String line;
while ((line = bufr.readLine()) != null) {
bufw.write(line);
bufw.newLine(); // 写入换行符
bufw.flush(); // 刷新缓冲区
}
} catch (IOException e) {
throw new RuntimeException("读写失败", e);
} finally {
try {
if (bufr != null) bufr.close();
if (bufw != null) bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节缓冲流
1. BufferedInputStream
java
// 构造方法
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)
2. BufferedOutputStream
java
// 构造方法
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out, int size)
字节缓冲流示例(音频复制)
java
import java.io.*;
public class BufferedCopyAudio {
public static void main(String[] args) {
BufferedInputStream bufis = null;
BufferedOutputStream bufos = null;
try {
bufis = new BufferedInputStream(new FileInputStream("source.mp3"));
bufos = new BufferedOutputStream(new FileOutputStream("target.mp3"));
int by;
while ((by = bufis.read()) != -1) {
bufos.write(by);
}
} catch (IOException e) {
throw new RuntimeException("复制失败", e);
} finally {
try {
if (bufis != null) bufis.close();
if (bufos != null) bufos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
转换流
转换流是字节流与字符流之间的桥梁,主要用于字符编码转换。
1. InputStreamReader(字节流→字符流)
java
// 构造方法
InputStreamReader(InputStream in) // 使用默认字符集
InputStreamReader(InputStream in, String charsetName) // 指定字符集
// 主要方法
getEncoding() // 返回字符编码名称
2. OutputStreamWriter(字符流→字节流)
java
// 构造方法
OutputStreamWriter(OutputStream out) // 使用默认字符集
OutputStreamWriter(OutputStream out, String charsetName) // 指定字符集
// 主要方法
getEncoding() // 返回字符编码名称
转换流应用示例
java
import java.io.*;
public class TransformStreamDemo {
public static void main(String[] args) {
BufferedReader bufr = null;
BufferedWriter bufw = null;
try {
// 系统输入转为字符流,使用UTF-8编码
bufr = new BufferedReader(
new InputStreamReader(System.in, "UTF-8")
);
// 系统输出转为字符流,使用UTF-8编码
bufw = new BufferedWriter(
new OutputStreamWriter(System.out, "UTF-8")
);
String line;
while ((line = bufr.readLine()) != null) {
if ("exit".equals(line)) {
break;
}
// 转为大写并输出
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
throw new RuntimeException("处理失败", e);
} finally {
try {
if (bufr != null) bufr.close();
if (bufw != null) bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
File类
File类用于封装文件或文件夹,提供对文件系统的操作功能。
构造方法
java
File(String pathname) // 通过路径名创建
File(String parent, String child) // 通过父目录和子文件名创建
File(File parent, String child) // 通过父File对象和子文件名创建
主要方法
创建和删除操作
| 方法 | 返回类型 | 描述 |
|---|---|---|
createNewFile() |
boolean | 创建新文件 |
mkdir() |
boolean | 创建目录 |
mkdirs() |
boolean | 创建目录(包括必需的父目录) |
delete() |
boolean | 删除文件或目录 |
判断操作
| 方法 | 返回类型 | 描述 |
|---|---|---|
exists() |
boolean | 判断是否存在 |
isFile() |
boolean | 判断是否为文件 |
isDirectory() |
boolean | 判断是否为目录 |
canRead() |
boolean | 判断是否可读 |
canWrite() |
boolean | 判断是否可写 |
获取信息
| 方法 | 返回类型 | 描述 |
|---|---|---|
getName() |
String | 获取文件名 |
getPath() |
String | 获取路径 |
getParent() |
String | 获取父目录 |
length() |
long | 获取文件大小 |
lastModified() |
long | 获取最后修改时间 |
目录操作
| 方法 | 返回类型 | 描述 |
|---|---|---|
list() |
String[] | 获取目录中文件名数组 |
listFiles() |
File[] | 获取目录中文件对象数组 |
File类应用示例
1. 条件查找文件
java
import java.io.*;
public class FileFilterDemo {
public static void main(String[] args) {
File dir = new File("C:\\Users\\Desktop");
// 使用FilenameFilter过滤条件
File[] jpgFiles = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jpg");
}
});
// 输出找到的JPG文件
if (jpgFiles != null) {
for (File file : jpgFiles) {
System.out.println(file.getName() + " - 大小: " + file.length() + " bytes");
}
}
}
}
2. 递归遍历目录
java
import java.io.*;
public class DirectoryTraversal {
public static void main(String[] args) {
File dir = new File("C:\\Projects");
traverseDirectory(dir, 0);
}
public static void traverseDirectory(File dir, int level) {
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 打印目录名(带缩进)
printWithIndent(dir.getName() + "/", level);
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
traverseDirectory(file, level + 1);
} else {
printWithIndent(file.getName(), level + 1);
}
}
}
}
private static void printWithIndent(String text, int level) {
for (int i = 0; i < level; i++) {
System.out.print(" ");
}
System.out.println(text);
}
}
特殊流类型
1. 打印流
打印流提供便捷的打印功能,可以打印各种数据类型。
PrintStream(字节打印流)
java
// 构造方法
PrintStream(File file)
PrintStream(OutputStream out)
PrintStream(String fileName)
// 特有方法
print(Object obj) // 打印对象
println(Object obj) // 打印对象并换行
printf(String format, Object... args) // 格式化打印
PrintWriter(字符打印流)
java
// 构造方法
PrintWriter(File file)
PrintWriter(Writer out)
PrintWriter(String fileName)
2. 对象流(序列化)
对象流用于对象的序列化和反序列化操作。
前提条件
被序列化的类必须实现Serializable接口:
java
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
// 构造方法、getter、setter...
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + ":" + age;
}
}
ObjectOutputStream(对象输出流)
java
import java.io.*;
public class ObjectSerialize {
public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("person.dat"))) {
Person person = new Person("张三", 25);
oos.writeObject(person);
System.out.println("对象序列化完成");
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectInputStream(对象输入流)
java
import java.io.*;
public class ObjectDeserialize {
public static void main(String[] args) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("person.dat"))) {
Person person = (Person) ois.readObject();
System.out.println("反序列化对象: " + person);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3. 数据流
数据流用于读写基本数据类型。
DataOutputStream
java
import java.io.*;
public class DataStreamWrite {
public static void main(String[] args) {
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream("data.dat"))) {
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeUTF("Hello World");
dos.writeBoolean(true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
DataInputStream
java
import java.io.*;
public class DataStreamRead {
public static void main(String[] args) {
try (DataInputStream dis = new DataInputStream(
new FileInputStream("data.dat"))) {
int num = dis.readInt();
double pi = dis.readDouble();
String str = dis.readUTF();
boolean flag = dis.readBoolean();
System.out.println("int: " + num);
System.out.println("double: " + pi);
System.out.println("String: " + str);
System.out.println("boolean: " + flag);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. RandomAccessFile
随机访问文件类,支持对文件的随机读写。
基本用法
java
import java.io.*;
public class RandomAccessFileDemo {
public static void main(String[] args) {
try {
// 写入数据
writeData();
// 读取数据
readData();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void writeData() throws IOException {
try (RandomAccessFile raf = new RandomAccessFile("random.dat", "rw")) {
raf.writeUTF("张三");
raf.writeInt(25);
raf.writeUTF("李四");
raf.writeInt(30);
}
}
public static void readData() throws IOException {
try (RandomAccessFile raf = new RandomAccessFile("random.dat", "r")) {
// 移动到指定位置读取
raf.seek(0);
String name1 = raf.readUTF();
int age1 = raf.readInt();
System.out.println(name1 + ": " + age1);
String name2 = raf.readUTF();
int age2 = raf.readInt();
System.out.println(name2 + ": " + age2);
}
}
}
IO异常处理
常见IO异常类型
| 异常类 | 描述 |
|---|---|
IOException |
IO操作的通用异常 |
FileNotFoundException |
文件未找到异常 |
EOFException |
意外到达文件或流末尾 |
UTFDataFormatException |
UTF格式数据异常 |
InvalidClassException |
序列化版本不匹配 |
IO异常处理最佳实践
1. try-with-resources(推荐)
java
public class IOExceptionHandling {
public static void copyFile(String source, String target) {
// 自动资源管理
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target)) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO操作失败: " + e.getMessage());
}
}
}
2. 传统异常处理方式
java
public class TraditionalExceptionHandling {
public static void copyFile(String source, String target) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(source);
fos = new FileOutputStream(target);
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO操作失败: " + e.getMessage());
} finally {
// 确保资源被正确关闭
try {
if (fis != null) fis.close();
} catch (IOException e) {
System.err.println("关闭输入流失败: " + e.getMessage());
}
try {
if (fos != null) fos.close();
} catch (IOException e) {
System.err.println("关闭输出流失败: " + e.getMessage());
}
}
}
}
总结
IO流选择指南
| 场景 | 推荐流类型 | 说明 |
|---|---|---|
| 文本文件读写 | FileReader/FileWriter + 缓冲流 | 字符流处理文本更方便 |
| 二进制文件操作 | FileInputStream/FileOutputStream + 缓冲流 | 字节流处理二进制数据 |
| 网络数据传输 | 字节流 + 转换流 | 网络传输基于字节 |
| 对象持久化 | ObjectInputStream/ObjectOutputStream | 序列化和反序列化 |
| 大文件处理 | 缓冲流 | 提高效率 |
| 随机访问 | RandomAccessFile | 支持随机定位 |
性能优化建议
- 使用缓冲流:减少系统调用次数,提高IO效率
- 合理设置缓冲区大小:根据文件大小和内存情况调整
- 及时关闭流:使用try-with-resources或finally块
- 批量操作:使用数组读写而非单个字节/字符
- 避免频繁的小数据读写:合并读写操作
最佳实践
- 资源管理:始终确保流被正确关闭
- 异常处理:捕获具体的异常类型并提供有意义的错误信息
- 编码处理:明确指定字符编码,避免乱码问题
- 线程安全:IO流通常不是线程安全的,多线程环境需要同步
- 内存管理:处理大文件时注意内存使用,避免一次性读取过多数据
通过掌握Java IO流的核心概念和常用类,可以高效地处理各种输入输出操作,为Java应用程序提供强大的文件处理能力。
