第七章 输入输出流
第一节 Java输入输出流基础
概述
本节主要讲解Java语言程序设计中输入输出流(I/O流)的核心知识。输入输出流是Java程序与外部数据交换的桥梁,掌握其原理和用法对于实现文件操作、数据传输等功能至关重要。通过本节的学习,考生将理解Java中输入输出流的基本概念、分类及其工作机制,掌握常用的输入输出流类及其使用方法,能够编写高效、规范的I/O操作代码,为后续高级编程打下坚实基础。
学习目标
- 理解Java输入输出流的基本概念和分类
- 掌握字节流和字符流的区别及应用场景
- 熟悉常用的输入输出流类及其构造方法
- 能够正确读取和写入文件数据
- 理解缓冲流的作用和使用方法
- 通过实例掌握文件复制、文本读取与写入的代码实现
核心概念
1. 输入输出流(I/O流)
输入输出流是Java中用于处理数据输入和输出的抽象概念,流表示数据的连续传输,输入流负责读取数据,输出流负责写出数据。
2. 字节流和字符流
- 字节流:以字节(8位)为单位读写数据,适合处理所有类型数据(如图片、视频、音频、文本文件等)。
- 字符流:以字符(16位Unicode)为单位读写数据,专门用于处理文本数据,自动处理字符编码转换。
3. 节点流与处理流
- 节点流(低级流):直接连接数据源或目的地,如文件、内存等。
- 处理流(高级流):对节点流进行包装,提供额外功能,如缓冲、数据转换、对象序列化等。
4. 缓冲流
缓冲流通过内置缓冲区减少实际读写次数,提高I/O效率。Java中常用的缓冲流有BufferedReader、BufferedWriter、BufferedInputStream和BufferedOutputStream。
原理分析
Java的输入输出流基于流的概念,体现了数据的顺序传输。通过抽象的InputStream和OutputStream(字节流基类),以及Reader和Writer(字符流基类)实现具体数据操作。流的设计模式遵循装饰者模式(Decorator Pattern),允许动态地将功能附加到流对象上,如缓冲、转换等。
- 字节流工作原理:通过读写字节数组完成数据传递,适合所有类型数据。读写时直接操作二进制数据。
- 字符流工作原理:通过字符缓冲区和字符编码转换,精确处理文本文件中字符的读写,避免乱码。
缓冲流原理在于内部维护一个缓冲区,读操作时先从缓冲区获取数据,写操作时先写入缓冲区,满时才一次性写入底层流,极大提升效率。
详细内容
1. 输入输出流的分类
Java I/O流主要分为两大类:
- 字节流
- 输入流:
InputStream及其子类 - 输出流:
OutputStream及其子类
- 输入流:
- 字符流
- 输入流:
Reader及其子类 - 输出流:
Writer及其子类
- 输入流:
| 流类型 | 作用 | 典型类 | 适用场景 |
|---|---|---|---|
| 字节输入流 | 读取字节数据 | FileInputStream, BufferedInputStream | 读取图片、音频等二进制文件 |
| 字节输出流 | 写出字节数据 | FileOutputStream, BufferedOutputStream | 写入视频、压缩文件等 |
| 字符输入流 | 读取字符数据 | FileReader, BufferedReader | 读取文本文件、配置文件 |
| 字符输出流 | 写出字符数据 | FileWriter, BufferedWriter | 写入文本内容 |
2. 读取文件的基本步骤
- 创建对应的输入流对象(如
FileInputStream或FileReader),并绑定目标文件 - 通过循环读取数据,通常使用
read()方法 - 处理读取的数据,如转换或保存
- 关闭输入流,释放资源
3. 写入文件的基本步骤
- 创建对应的输出流对象(如
FileOutputStream或FileWriter),并绑定目标文件 - 使用
write()方法写入数据 - 关闭输出流,确保数据刷新并释放资源
4. 缓冲流的使用
缓冲流在节点流基础上包装,常见用法:
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"));
缓冲流提升性能,减少磁盘访问次数,同时提供便捷方法,如readLine()读取一行数据。
5. 常用方法详解
read():读取一个字节或字符,返回int类型,读取结束返回-1read(byte[] b)或read(char[] cbuf):批量读取数据,提升效率write(int b):写入一个字节或字符write(char[] cbuf, int off, int len):写入字符数组部分内容flush():强制将缓冲区数据写出,常在写操作后调用保证数据完整close():关闭流,释放资源,关闭前会自动调用flush()
典型实例
实例一:使用字节流复制文件
import java.io.*;
public class FileCopyByteStream {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("source.jpg");
fos = new FileOutputStream("copy.jpg");
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
System.out.println("文件复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null) fis.close();
if (fos != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
分析:
- 使用
FileInputStream和FileOutputStream完成二进制文件复制 - 采用缓冲数组提高复制效率
- 使用
finally块确保资源释放
结论:字节流适合处理非文本文件,复制效率依赖缓冲大小和读取方式。
实例二:使用字符流读取文本文件
import java.io.*;
public class ReadTextFile {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("test.txt"));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
分析:
- 结合
FileReader和BufferedReader实现高效文本读取 - 使用
readLine()按行读取,便于逐行处理文本
结论:字符流和缓冲流适合处理文本文件,简化读取操作。
实例三:写入文本文件内容
import java.io.*;
public class WriteTextFile {
public static void main(String[] args) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter("output.txt"));
bw.write("Java输入输出流学习资料");
bw.newLine();
bw.write("欢迎学习全国计算机等级考试二级Java部分");
bw.flush();
System.out.println("写入成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
分析:
- 使用
BufferedWriter写入字符串 newLine()方法写入换行符,保证跨平台兼容flush()确保数据及时写入
结论:字符输出流写文本更直观,缓冲流增强效率和功能。
常见误区
误区:忽略流的关闭
- 问题:未关闭流导致资源泄露,可能引起程序异常或性能下降。
- 正确做法:使用
try-with-resources或finally块关闭流。
误区:错误使用字符流读取二进制文件
- 问题:字符流会对字节进行编码转换,导致二进制文件损坏。
- 正确做法:处理二进制文件时应使用字节流。
误区:未调用
flush()导致数据丢失- 问题:缓冲流写数据后未调用
flush(),数据未及时写入文件。 - 正确做法:写操作结束后调用
flush()或关闭流。
- 问题:缓冲流写数据后未调用
误区:一次读取一个字节/字符效率低
- 问题:使用单字节/字符读取大量数据,效率低且耗时长。
- 正确做法:使用带缓冲的批量读取方法,如
read(byte[] b)或read(char[] cbuf)。
误区:混淆输入输出流类和接口
- 问题:不理解
InputStream、OutputStream和Reader、Writer的区别,导致选错流类型。 - 正确做法:明确字节流和字符流的应用场景,合理选择流对象。
- 问题:不理解
应用场景
- 文本文件操作:读取配置文件、日志文件,写入程序输出结果
- 二进制文件处理:图片、音频、视频文件的复制和传输
- 网络数据传输:Socket通信中流数据的读取和写入
- 数据缓存与优化:使用缓冲流提升文件读写性能
- 序列化与反序列化:对象流的读写实现数据持久化
知识拓展
Java NIO(New I/O)包简介
- 相较传统IO,NIO引入了通道(Channel)、缓冲区(Buffer)和选择器(Selector),支持非阻塞I/O和更高效的数据处理。
对象流(Object Streams)
- 利用
ObjectInputStream和ObjectOutputStream实现Java对象的序列化与反序列化,方便对象的持久化存储和网络传输。
- 利用
数据流(Data Streams)
DataInputStream和DataOutputStream支持基本数据类型的读写,避免数据转换错误。
字符编码和解码
- 了解UTF-8、UTF-16等编码格式及其对字符流的影响,掌握编码转换技巧,避免乱码问题。
总结回顾
本节系统介绍了Java输入输出流的基础知识和应用方法。首先明确了输入输出流的分类和核心概念,详细分析了字节流与字符流的区别和适用场景。随后讲解了流的工作原理,重点突出缓冲流的提升效率机制。通过典型实例,展示了文件复制、文本读取和写入的具体实现步骤。总结了常见误区,帮助考生避免常见错误。最后结合实际应用场景和知识拓展,增强理解和运用能力。考生应熟练掌握各种流的使用技巧,做到灵活应用、规范编程,为全国计算机等级考试二级Java语言编程部分奠定坚实基础。