第八章 文件与输入输出流 - 第一节 文件与输入输出流基础
概述
本节内容主要介绍文件与输入输出流的基本概念和使用方法,作为面向对象程序设计中重要的内容之一,掌握文件操作和输入输出流的相关知识对于实现数据的持久化存储和程序与外界的数据交互至关重要。通过本节的学习,考生将能够理解文件的本质,掌握Java中流的体系结构,学会使用字符流和字节流进行文件的读写操作,并能够处理文件输入输出过程中的异常。
学习目标:
- 理解文件和输入输出流的基本概念
- 掌握Java中输入输出流的分类及工作原理
- 熟练使用文件的读写操作
- 了解常见的文件操作异常及处理方法
- 通过实例强化理解和应用能力
核心概念
文件 (File)
文件是存储在存储介质(如硬盘、SSD)上的数据集合,是数据的持久化存储单位。文件具有路径、名称和扩展名等属性。
输入流 (InputStream / Reader)
输入流是程序读取数据的通道,用于从文件、键盘、网络等源头读取数据。Java中输入流分为字节输入流和字符输入流:
- 字节输入流:InputStream及其子类,按字节读取数据,适合处理二进制数据。
- 字符输入流:Reader及其子类,按字符读取数据,适合处理文本数据。
输出流 (OutputStream / Writer)
输出流是程序写出数据的通道,用于向文件、显示器、网络等目标写入数据。Java中输出流也分为字节输出流和字符输出流:
- 字节输出流:OutputStream及其子类,按字节写出数据。
- 字符输出流:Writer及其子类,按字符写出数据。
缓冲流 (BufferedStream)
缓冲流通过内部缓冲区提高读写效率,常用的有BufferedReader、BufferedWriter、BufferedInputStream和BufferedOutputStream。
原理分析
文件与流的关系
文件作为数据的持久化存储介质,程序无法直接访问它的内容,而是通过输入流和输出流来读写文件数据。流是一种抽象的概念,表示数据的传输通道。数据从源头通过输入流进入程序,从程序通过输出流写入目标。
字节流与字符流
- 字节流处理单位是字节,适合所有类型数据,尤其是图片、音频、视频等二进制文件。
- 字符流处理单位是字符,适合文本文件,自动处理字符编码转换(如UTF-8),更方便文本数据处理。
流的工作机制
流通常以顺序方式读写数据,输入流从源头顺序读取,输出流顺序写入。为了提高效率,缓冲流引入了缓冲区,减少物理设备访问次数。
异常处理
文件操作容易出现IOException,如文件不存在、权限不足等,必须使用异常处理机制保证程序健壮。
详细内容
1. 文件类File的使用
Java中File类用于表示文件或目录的路径信息,提供了文件的创建、删除、重命名、判断属性等方法。
- 创建File对象
File file = new File("path/to/file.txt"); - 判断文件是否存在
boolean exists = file.exists(); - 获取文件属性
String name = file.getName(); long length = file.length(); boolean isDirectory = file.isDirectory(); - 创建新文件和目录
file.createNewFile(); file.mkdir();
注意:File类不直接操作文件内容,只操作文件路径和属性。
2. 字节输入输出流
字节输入流InputStream
- 用于读取二进制数据,常用子类有FileInputStream。
- 读取方法:
int read()返回一个字节,返回-1表示文件末尾。
示例读取文件内容:
try (FileInputStream fis = new FileInputStream("test.dat")) {
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
} catch (IOException e) {
e.printStackTrace();
}
字节输出流OutputStream
- 用于写入二进制数据,常用子类有FileOutputStream。
- 写入方法:
void write(int b)写出一个字节。
示例写入数据:
try (FileOutputStream fos = new FileOutputStream("output.dat")) {
fos.write("Hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
3. 字符输入输出流
字符输入流Reader
- 用于读取文本数据,常用子类有FileReader。
- 读取方法:
int read()返回一个字符的Unicode码,返回-1表示末尾。
示例读取文本文件:
try (FileReader fr = new FileReader("test.txt")) {
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
字符输出流Writer
- 用于写入文本数据,常用子类有FileWriter。
- 写入方法:
void write(int c)写出一个字符。
示例写入文本文件:
try (FileWriter fw = new FileWriter("output.txt")) {
fw.write("Hello World");
} catch (IOException e) {
e.printStackTrace();
}
4. 缓冲流的使用
缓冲流提高了读写效率,常用的有:
- BufferedReader:带缓冲的字符输入流,提供了
readLine()方法,方便按行读取文本。 - BufferedWriter:带缓冲的字符输出流,提供
newLine()方法写入换行符。
示例按行读取和写入:
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();
}
5. 流的关闭与异常处理
- 使用
try-with-resources语句确保流自动关闭,避免资源泄露。 - 捕获IOException,处理文件不存在、权限不足等异常。
实例分析
实例一:复制文本文件
背景:将一个文本文件内容复制到另一个文件中。
代码实现:
public static void copyTextFile(String src, String dest) {
try (BufferedReader br = new BufferedReader(new FileReader(src));
BufferedWriter bw = new BufferedWriter(new FileWriter(dest))) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
分析:利用缓冲字符流按行读取和写入,代码简洁且效率较高。
结论:缓冲流适合文本文件复制,保证了性能和代码易读性。
实例二:读取二进制文件(图片)
背景:读取图片文件,统计文件字节数。
代码实现:
public static void countFileBytes(String filePath) {
int count = 0;
try (FileInputStream fis = new FileInputStream(filePath)) {
while (fis.read() != -1) {
count++;
}
System.out.println("文件字节数:" + count);
} catch (IOException e) {
e.printStackTrace();
}
}
分析:使用字节输入流逐字节读取,适合二进制文件。
结论:字节流适合处理非文本文件,能精确读取所有二进制数据。
实例三:写入用户输入内容到文件
背景:从控制台读取用户输入内容,并写入文件。
代码实现:
public static void writeUserInputToFile(String filePath) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter(filePath))) {
System.out.println("请输入内容,输入exit结束:");
String line;
while (!(line = br.readLine()).equalsIgnoreCase("exit")) {
bw.write(line);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
分析:结合了标准输入流和文件输出流,演示了流的灵活组合。
结论:流的组合使用提升了程序的交互性和功能性。
常见误区
误区:File类用于读写文件内容
- 正确做法:File类只表示文件路径和属性,读写文件内容需使用流。
误区:输入输出流不需要关闭
- 正确做法:必须关闭流,释放资源,推荐使用try-with-resources自动关闭。
误区:用字符流读写二进制文件
- 正确做法:二进制文件应使用字节流,字符流适合文本文件。
误区:不处理异常直接抛出
- 正确做法:应捕获并处理IOException,保证程序健壮性。
误区:使用read()方法读取字符时直接转换为char输出
- 正确做法:read()返回int,-1表示文件末尾,转换前应判断。
应用场景
数据持久化存储
- 将程序中的数据保存为文件,实现数据的永久保存。
配置文件读取
- 读取程序配置文件,实现程序参数化和灵活配置。
日志记录
- 程序运行日志写入文件,便于调试和监控。
文件传输与复制
- 通过流实现文件的复制和传输功能。
用户输入输出交互
- 从键盘读取输入,并将结果输出到文件或屏幕。
知识拓展
随机访问文件(RandomAccessFile)
- 允许程序跳转到文件的任意位置读写,适合复杂文件操作。
对象流(ObjectInputStream/ObjectOutputStream)
- 用于对象的序列化和反序列化,实现对象持久化。
文件通道(FileChannel)
- NIO库中的文件操作方式,性能优于传统流。
字符编码
- 理解字符流内的编码转换机制,避免乱码问题。
总结回顾
本节围绕文件与输入输出流展开,重点讲解了文件的基本概念和Java中流的体系结构,包括字节流和字符流的区别与应用,缓冲流的优势及使用,文件操作的异常处理等。通过典型实例,考生能够掌握文件的读写操作方法,理解流的工作原理和使用规范,避免常见误区,增强实际编程能力。
掌握本节内容后,考生能灵活应用文件与流进行数据持久化和程序数据交互,为后续更复杂的文件处理和数据管理打下坚实基础。