【Java】转换流详解

大家好,本篇继续更新 Java IO 流系列内容,前面我们讲解了字节流、字符流,今天重点来讲转换流。在实际开发中,字节流与字符流互转、文件读写乱码等问题,基本都靠转换流来解决。

一、转换流的核心作用

Java IO 分为字节流字符流两大类:

  • 字节流:万能流,可操作所有类型文件,但直接读写文本容易出现编码问题
  • 字符流:专门用来读写文本文件,基于字符操作

转换流就是两者之间的桥梁,主要包含两个核心类:

  1. InputStreamReader字节输入流 → 字符输入流
  2. OutputStreamWriter字符输出流 → 字节输出流

二、编码、解码与字符集

想要理解转换流,先要搞懂编码、解码和字符集,这也是乱码产生的根源。

1. 编码与解码

简单总结:

  • 编码:字符(人能看懂)→ 字节(计算机存储)
  • 解码:字节(计算机存储)→ 字符(人能看懂)

文本在计算机中最终都是以二进制字节形式保存,读写时就会经历编码和解码两个过程。

2. 常见字符集

不同字符集对应不同的编码规则,日常开发高频使用:

  • ASCII:仅支持英文字母、数字、基础符号
  • GBK:国内常用中文编码,兼容 GB2312,汉字占 2 个字节
  • UTF-8:互联网主流编码,通用多国语言,汉字占 3 个字节

3. 乱码产生原因

编码和解码使用的字符集不一致,是乱码的唯一核心原因。

举个例子:文件按照 GBK 编码保存,读取时却用 UTF-8 解析,最终就会出现乱码。而转换流最大的价值,就是可以手动指定读写的字符集,从根源解决乱码。

三、InputStreamReader 字节转字符输入流

InputStreamReaderReader 的子类,负责把字节输入流包装为字符输入流,支持自定义字符集。

1. 常用构造方法

  1. InputStreamReader(InputStream in):使用系统默认字符集
  2. InputStreamReader(InputStream in, String charsetName)指定字符集(推荐)

2. 代码示例(读取指定编码文件)

下面演示用 InputStreamReaderGBK 编码读取本地文本文件:

import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;

public class InputStreamReaderDemo {
    public static void main(String[] args) {
        // 1. 创建字节输入流
        try (FileInputStream fis = new FileInputStream("test.txt");
             // 2. 字节流转为字符流,指定编码为GBK
             InputStreamReader isr = new InputStreamReader(fis, "GBK")) {

            // 定义字符数组接收数据
            char[] chars = new char[1024];
            int len;
            // 循环读取文本
            while ((len = isr.read(chars)) != -1) {
                System.out.print(new String(chars, 0, len));
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、OutputStreamWriter 字符转字节输出流

OutputStreamWriterWriter 的子类,负责把字符输出流包装为字节输出流,写入文件时可指定编码。

1. 常用构造方法

  1. OutputStreamWriter(OutputStream out):使用系统默认字符集
  2. OutputStreamWriter(OutputStream out, String charsetName)指定字符集(推荐)

2. 代码示例(按指定编码写入文件)

演示以 UTF-8 编码向文件写入文本:

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;

public class OutputStreamWriterDemo {
    public static void main(String[] args) {
        // 1. 创建字节输出流
        try (FileOutputStream fos = new FileOutputStream("output.txt");
             // 2. 字符流转字节流,指定编码UTF-8
             OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")) {

            // 写入文本内容
            String content = "Java转换流实战,解决中文乱码问题";
            osw.write(content);
            // 刷新缓冲区,确保数据写入文件
            osw.flush();
            System.out.println("写入完成");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

五、综合案例:读写统一字符集,彻底避免乱码

实际开发中,写入和读取必须使用相同字符集。下面写一个完整案例:先用 GBK 写入文件,再用 GBK 读取文件。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;

public class ConvertStreamTest {
    public static void main(String[] args) {
        // 1. 以GBK编码写入文件
        try (FileOutputStream fos = new FileOutputStream("gbkFile.txt");
             OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK")) {

            osw.write("测试中文内容:Java IO 转换流");
            osw.flush();
            System.out.println("GBK编码写入成功");

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 2. 以GBK编码读取文件
        try (FileInputStream fis = new FileInputStream("gbkFile.txt");
             InputStreamReader isr = new InputStreamReader(fis, "GBK")) {

            char[] buf = new char[1024];
            int length = isr.read(buf);
            System.out.println("读取内容:" + new String(buf, 0, length));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行后可以正常展示中文,不会出现乱码;如果把读取编码改为 UTF-8,立刻就会出现乱码,大家可以自行测试对比。

六、总结

  1. 转换流分为 InputStreamReaderOutputStreamWriter,是字节流与字符流的转换桥梁。
  2. 文本乱码的本质:编码、解码字符集不统一,转换流的核心作用就是手动指定编码。
  3. 开发规范:操作文本文件时,尽量主动声明字符集,不要依赖系统默认编码,保证程序跨平台稳定。
  4. 使用完毕记得关闭流,推荐使用 try-with-resources 语法,自动释放资源。

本篇 Java 转换流内容就分享到这里,下一篇我们继续更新 IO 流相关知识点,感兴趣的朋友可以持续关注。

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*