1. Java IO 类概述
1.1 什么是 IO
IO 是 Input/Output(输入/输出) 的缩写,代表了程序与外部环境(如文件、网络、设备等)之间进行数据交换的操作。在 Java 中,IO 操作广泛用于文件读取、写入、网络通信等场景。Java 提供了强大的 IO 类来简化这些操作,并按照不同的数据类型(如字节、字符)和数据源提供了不同的类。
1.2 Java 常用的 IO 类
根据数据处理的方式不同,Java 中的 IO 操作可以分为以下几大类:
- 字节流:处理二进制数据,适用于图片、音频、视频等非文本数据。
- 字符流:处理文本数据,适用于字符串、字符等文本文件。
Java IO 常用类如下表:
类别 | 输入类 | 输出类 |
---|---|---|
字节流 | InputStream | OutputStream |
文件字节流 | FileInputStream | FileOutputStream |
字符流 | Reader | Writer |
文件字符流 | FileReader | FileWriter |
缓冲流 | BufferedInputStream | BufferedOutputStream |
缓冲字符流 | BufferedReader | BufferedWriter |
2. 字节流与字符流的区别
Java 中的 IO 操作分为两类:字节流 和 字符流。它们的区别在于处理的数据类型不同。
2.1 什么是字节流
字节流 以 8 位字节 为单位读取或写入数据,适合处理 二进制数据。字节流的常见用法是处理图片、视频、音频等非文本文件。字节流类的主要父类有:
InputStream
:字节输入流,用于从数据源读取字节。OutputStream
:字节输出流,用于将字节写入目标。
常用字节流类包括 FileInputStream
、FileOutputStream
、BufferedInputStream
、BufferedOutputStream
等。
2.2 什么是字符流
字符流 以 16 位字符 为单位进行数据处理,适合处理 文本数据,如字符串和文本文件。字符流类的主要父类有:
Reader
:字符输入流,用于读取字符数据。Writer
:字符输出流,用于写入字符数据。
常用字符流类包括 FileReader
、FileWriter
、BufferedReader
、BufferedWriter
等。
2.3 字节流与字符流的区别
区别点 | 字节流 | 字符流 |
---|---|---|
处理的数据类型 | 处理二进制数据,如图片、音频 | 处理文本数据,如文本文件、字符串 |
数据单位 | 以字节为单位(8 位) | 以字符为单位(16 位) |
使用场景 | 适用于非文本文件的处理 | 适用于文本文件或字符数据的处理 |
父类 | InputStream 和 OutputStream | Reader 和 Writer |
常用类 | FileInputStream ,FileOutputStream | FileReader ,FileWriter |
3. Java IO 常用字节流类
字节流主要用于处理非文本数据。Java 提供了丰富的字节流类来帮助开发者进行文件或网络数据的操作。
3.1 InputStream 类
InputStream
是字节输入流的父类,定义了从不同的输入源(如文件、网络、内存缓冲区)读取字节的基本方法。
常用的 InputStream
子类包括:
FileInputStream
:用于从文件中读取字节数据。BufferedInputStream
:为其他输入流提供缓冲功能,提高读取效率。ByteArrayInputStream
:从字节数组中读取数据。
3.2 OutputStream 类
OutputStream
是字节输出流的父类,定义了向不同的输出目标(如文件、网络、内存缓冲区)写入字节的基本方法。
常用的 OutputStream
子类包括:
FileOutputStream
:用于将字节数据写入文件。BufferedOutputStream
:为其他输出流提供缓冲功能,提高写入效率。ByteArrayOutputStream
:向字节数组中写入数据。
3.3 FileInputStream 和 FileOutputStream 示例
示例:读取二进制文件
通过 FileInputStream
读取二进制文件(如图片),并将其保存到字节数组中:
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputExample {
public static void main(String[] args) {
String filePath = "image.jpg"; // 文件路径
try (FileInputStream fis = new FileInputStream(filePath)) {
byte[] data = new byte[fis.available()];
fis.read(data); // 将文件内容读取到字节数组中
System.out.println("读取了 " + data.length + " 个字节的数据");
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例:写入二进制文件
通过 FileOutputStream
将字节数据写入文件:
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputExample {
public static void main(String[] args) {
String filePath = "output.jpg"; // 输出文件路径
byte[] data = { /* 图片的字节数据 */ };
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(data); // 将字节数据写入文件
System.out.println("写入了 " + data.length + " 个字节的数据");
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这两个例子中,使用了 try-with-resources
语法来确保流的正确关闭,防止资源泄漏。
4. Java IO 常用字符流类
字符流用于处理文本数据,如字符串或文本文件的读取和写入。字符流以 Reader
和 Writer
为父类,分别对应输入和输出操作。
4.1 Reader 类
Reader
是字符输入流的父类,用于从各种输入源(如文件、内存缓冲区、网络连接)读取字符数据。
常用的 Reader
子类包括:
FileReader
:从文件中读取字符数据。BufferedReader
:提供缓冲功能以提高读取效率。
4.2 Writer 类
Writer
是字符输出流的父类,用于将字符数据写入输出目标(如文件、内存缓冲区、网络连接)。
常用的 Writer
子类包括:
FileWriter
:向文件写入字符数据。BufferedWriter
:提供缓冲功能以提高写入效率。
4.3 FileReader 和 FileWriter 示例
示例:读取文本文件
通过 FileReader
和 BufferedReader
读取文本文件的内容:
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
public class FileReaderExample {
public static void main(String[] args) {
String filePath = "example.txt"; // 文件路径
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line); // 输出文件的每一行
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例:写入文本文件
通过 FileWriter
和 BufferedWriter
将文本数据写入文件:
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;
public class FileWriterExample {
public static void main(String[] args) {
String filePath = "output.txt"; // 输出文件路径
String content = "这是写入文件的文本内容。";
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filePath))) {
bw.write(content); // 写入文本内容
bw.newLine(); // 换行
bw.write("这是另一行文本。");
System.out.println("文本内容已成功写入文件。");
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. Java 读取和写入文本的综合示例
结合读取和写入操作,展示如何读取一个文件的内容,并将其写入到另一个文件中。
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;
public class FileCopyExample {
public static void main(String[] args) {
String inputFilePath = "input.txt"; // 输入文件路径
String outputFilePath = "output.txt"; // 输出文件路径
try (
BufferedReader br = new BufferedReader(new FileReader(inputFilePath));
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath))
) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line); // 将读取的行写入输出文件
bw.newLine(); // 写入换行符
}
System.out.println("文件内容已成功复制到新的文件。");
} catch (IOException e) {
e.printStackTrace();
}
}
}
此示例通过 BufferedReader
读取文本文件,并通过 BufferedWriter
将内容写入到另一个文件中,确保了高效读取和写入操作。
6. 流泄漏问题及如何正确关闭流
6.1 流泄漏问题
在进行 IO 操作时,未能正确关闭流对象会导致资源泄漏问题。Java 的 IO 操作通常涉及文件句柄、网络连接等有限资源,如果这些资源未能及时释放,可能会引发以下问题:
- 文件锁未释放:如果文件流未关闭,文件可能仍被锁定,导致其他程序无法访问该文件。
- 资源消耗过高:长时间不释放 IO 资源会导致系统资源(如文件句柄)耗尽,从而引发性能问题或系统崩溃。
6.2 如何正确关闭流
为了解决流泄漏问题,Java 提供了多种方法来确保流在使用后被正确关闭:
方法1:显式关闭流
使用 try-finally
结构手动关闭流。
import java.io.FileInputStream;
import java.io.IOException;
public class ExplicitCloseExample {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("example.txt");
// 执行读取操作
} catch (IOException e) {
e.printStackTrace();
} finally {
// 确保在 finally 中关闭流
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
方法2:使用 try-with-resources
从 Java 7 开始,提供了 try-with-resources
语法,该结构可以自动关闭实现了 AutoCloseable
接口的资源类,避免了手动关闭流的麻烦。
import java.io.FileInputStream;
import java.io.IOException;
public class TryWithResourcesExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("example.txt")) {
// 执行读取操作
} catch (IOException e) {
e.printStackTrace();
}
// 文件流会自动关闭
}
}
try-with-resources
是推荐的方式,它能够确保即使发生异常,也会自动关闭流,极大地减少了流泄漏的可能性。