java io体系的根本分水岭是字节与字符两种数据单位:inputstream/outputstream处理8位字节,适用于二进制数据;reader/writer处理16位unicode字符,专为文本设计,内置编码转换能力。

Java IO 体系最根本的分水岭,就在于“字节”和“字符”两种数据单位的选择。InputStream 和 OutputStream 处理的是原始的 8 位字节(byte),而 Reader 和 Writer 处理的是 16 位 Unicode 字符(char)。这个差异直接决定了它们适用的场景、编码处理方式以及底层行为逻辑。
数据单位与适用场景不同
字节流面向二进制数据,不关心内容含义,适合处理图片、音频、视频、序列化对象、网络协议包等任意格式文件。字符流则专为文本设计,天然支持字符编码转换,更适合读写 .txt、.java、.xml、.json 等人类可读的文本内容。
- InputStream.read() 返回 int(0–255 或 -1),每次读一个字节
- Reader.read() 返回 int(0–65535 或 -1),每次读一个字符(可能由多个字节解码而来)
- 用 FileInputStream 直接读中文文本,很可能得到乱码;而 FileReader 默认按平台编码解析,能正确还原汉字
编码处理机制截然不同
字符流内部隐含编码逻辑,而字节流完全回避编码问题。Reader/Writer 本身不持有字符集,但它的实现类(如 InputStreamReader / OutputStreamWriter)必须指定 charset,才能在字节与字符之间建立可靠映射。
- FileReader 是 InputStreamReader 的简化封装,默认使用 Charset.defaultCharset(),不可控
- 推荐显式使用 new InputStreamReader(new FileInputStream("a.txt"), StandardCharsets.UTF_8)
- OutputStreamWriter 可将字符串按指定编码转为字节,再交由 FileOutputStream 写出
- 跳过编码环节直接操作字节流,就等于把编码责任甩给调用方——容易出错且难以调试
类继承结构体现设计意图
四者均为抽象基类,位于 java.io 包顶层,构成整个 IO 体系的骨架:
- InputStream / OutputStream:子类包括 FileInputStream、ByteArrayInputStream、BufferedInputStream 等,全部围绕 byte[] 操作展开
- Reader / Writer:子类包括 FileReader、StringReader、BufferedReader、PrintWriter 等,核心方法签名均以 char[] 或 String 为参数或返回值
- InputStreamReader 和 OutputStreamWriter 是关键桥梁类,属于“转换流”,把字节流升级为字符流
- BufferedReader 提供 readLine(),PrintWriter 支持 println() —— 这些便利方法只存在于字符流体系中
异常与资源管理需统一注意
所有四类流都声明 throws IOException,且都支持 try-with-resources 自动关闭。但要注意:关闭外层包装流(如 BufferedReader)会自动关闭其装饰的底层流(如 FileInputStream),无需重复 close。
- 不要单独关闭被包装的流,否则可能引发 IOException 或资源泄漏
- 字符流若未正确指定编码,即使编译运行无报错,也可能在不同环境出现乱码,属于运行时逻辑错误
- 字节流操作二进制更“安全”,但处理文本时需手动编码/解码,代码冗长且易错
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











