Java学习之——“IO流“的进阶流之转换流的学习
在博主的上一篇博文中,详细的介绍了“IO”流中最基本的一些知识,包括基本的常见的字节流和字符流,以及对应的缓冲流,对于“IO”流基础知识相对薄弱的同学可以先去看博主的上一篇博文Java学习之——万字详解“IO流”中基本的字节流和字符流 后在回头来看这篇进阶文章效果会更好。
Java IO流中的“进阶”流:转换流、序列化流、打印流和压缩流。它们构建在基础字节流和字符流之上,提供了更强大、更便捷的功能。
一、转换流
1.核心概念:为什么需要转换流?
要理解转换流,首先要明白 Java I/O 流体系中的两个核心分支:
- 字节流:以字节(8 bit)为基本单位,处理所有类型的数据(如图片、视频、音频等二进制文件,也包括文本文件)。基类是
InputStream
和OutputStream
。 - 字符流:以字符(16 bit,一个char)为基本单位,专门为方便处理文本数据而设计。它底层仍然是字节操作,但会自动处理字符编码。基类是
Reader
和Writer
。
问题:我们经常遇到这样的场景——一个字节流(例如,来自网络、文件的 FileInputStream
或 Socket.getInputStream()
)传输的是文本内容。如果我们直接使用字节流读取,得到的是一个个字节,我们需要自己将这些字节按照正确的字符编码(如 UTF-8, GBK)拼接和转换成字符,非常麻烦且容易出错。
解决方案:转换流(InputStreamReader
/ OutputStreamWriter
)。
它们的作用就是作为一座“桥梁”,将底层的字节流 转换为顶层的字符流,并在转换过程中完成字节到字符的编码解码工作。
InputStreamReader
: 将一个字节输入流(InputStream
)转换为一个字符输入流(Reader
)。【解码:字节 -> 字符】OutputStreamWriter
: 将一个字符输出流(Writer
)转换为一个字节输出流(OutputStream
)。【编码:字符 -> 字节】
2.InputStreamReader 详解
核心功能:InputStreamReader是 Reader的子类。它包裹着一个字节输入流(InputStream
),并读取字节,然后使用指定的或默认的字符集将其解码为字符。
关键构造函数:
// 使用默认字符集创建一个 InputStreamReader
InputStreamReader(InputStream in)// 使用指定的字符集名称创建一个 InputStreamReader
// 字符集如:"UTF-8", "GBK", "ISO-8859-1"
InputStreamReader(InputStream in, String charsetName)// 使用指定的 Charset 对象创建一个 InputStreamReader
InputStreamReader(InputStream in, Charset cs)
工作原理:
当你调用 InputStreamReader
的 read()
方法时,会发生以下步骤:
- 它从内部的
InputStream
中读取一个或多个字节。 - 这些字节根据构造时指定的字符集(Charset)进行解码。
- 将解码后的结果以一个
char
(或多个char
放入数组)的形式返回。
代码示例:
假设我们有一个文本文件 text.txt,其编码是 GBK。如果我们用默认字符集(通常是 UTF-8)的 FileReader(它是 InputStreamReader 的子类)读取,可能会乱码。使用 InputStreamReader 指定编码可以完美解决。
import java.io.*;public class InputStreamReaderDemo {public static void main(String[] args) {try (// 1. 创建一个字节流 FileInputStreamFileInputStream fis = new FileInputStream("text.txt");// 2. 创建一个转换流 InputStreamReader,并指定编码为 GBKInputStreamReader isr = new InputStreamReader(fis, "GBK");// 3. 为了高效,通常用 BufferedReader 包裹 InputStreamReaderBufferedReader br = new BufferedReader(isr)) {String line;// 此时读取到的字符已经是正确解码后的文本,不会乱码while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}
}
3.OutputStreamWriter 详解
OutputStreamWriter 是 Writer 的子类。它接收字符,然后使用指定的或默认的字符集将其编码为字节,并写入到底层的字节输出流(OutputStream)中。
关键构造函数
// 使用默认字符集创建一个 OutputStreamWriter
OutputStreamWriter(OutputStream out)// 使用指定的字符集名称创建一个 OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName)// 使用指定的 Charset 对象创建一个 OutputStreamWriter
OutputStreamWriter(OutputStream out, Charset cs)
工作原理
当你调用 OutputStreamWriter
的 write()
方法时,会发生以下步骤:
- 你传入的字符(或字符串)根据构造时指定的字符集(Charset)进行编码。
- 编码后得到的一个或多个字节被写入到底层的
OutputStream
中。 OutputStream
最终将这些字节写入到目标(文件、网络等)。
我们想要将一个字符串以 GBK
编码写入到文件中。
import java.io.*;public class OutputStreamWriterDemo {public static void main(String[] args) {try (// 1. 创建一个字节流 FileOutputStreamFileOutputStream fos = new FileOutputStream("output.txt");// 2. 创建一个转换流 OutputStreamWriter,并指定编码为 GBKOutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");// 3. 为了高效和方便,通常用 BufferedWriter 包裹BufferedWriter bw = new BufferedWriter(osw)) {String content = "你好,世界!";bw.write(content);bw.newLine();bw.write("This is a test.");} catch (IOException e) {e.printStackTrace();}// 最终文件 output.txt 的编码将是 GBK}
}
与 FileReader / FileWriter 的关系
-
FileReader
本质上是InputStreamReader
的子类,它简化了文件的读取,但无法指定编码,只能使用默认编码。FileWriter
同理。 -
结论:在需要明确指定编码的场合(绝大多数国际化和跨平台场景),不要直接使用
FileReader
和FileWriter
,而应该使用InputStreamReader
和OutputStreamWriter
并手动指定编码。FileReader/FileWriter
仅适用于处理与系统默认编码一致的文件。