Java IO 核心:BufferedReader/BufferedWriter & PrintStream/PrintWriter 技术笔记

Java IO 核心:BufferedReader/BufferedWriter & PrintStream/PrintWriter 技术笔记

一、笔记概述

本文聚焦 Java IO 体系中高频使用的 4 个字符/字节流工具类(BufferedReader、BufferedWriter、PrintStream、PrintWriter),从核心定位、功能差异、适用场景、代码示例、拓展技巧五个维度拆解,帮助理解其设计逻辑与实际应用,解决“该用哪个流写/读数据”的核心问题。

二、核心定位与设计目标

这 4 个类分为两大阵营,核心设计目标差异显著:

1. 缓冲流阵营(BufferedReader + BufferedWriter)

属于 Java IO 底层高效字符流,核心目标是提升字符读写效率。通过内置字符缓冲区减少磁盘/网络 IO 次数(避免每次读写都触发底层硬件操作),是处理文本文件“基础且高效”的核心工具,偏底层、偏严谨。

2. 打印流阵营(PrintStream + PrintWriter)

属于 Java IO 上层易用型流,核心目标是便捷地格式化输出数据。屏蔽了繁琐的 IO 异常处理,支持所有基本数据类型(int、double、boolean 等)和对象的直接打印,还提供格式化输出(printf),是面向“业务输出、日志打印、控制台展示”的易用工具,偏上层、偏实用。

三、逐个拆解:功能、示例、适用场景

1. BufferedReader(字符缓冲输入流)

核心功能
  • 包装底层字符输入流(如 FileReader),通过缓冲区提升文本读取效率;
  • 提供核心方法 readLine(),可直接按行读取文本(返回一行字符串,null 表示读取完毕),解决了“手动拼接字符读行”的繁琐;
  • 所有 IO 操作都会抛出 IOException,必须手动处理(try-catch 或 throws)。
代码示例(按行读取文本文件)
importjava.io.BufferedReader;importjava.io.FileReader;importjava.io.IOException;publicclassBufferedReaderDemo{publicstaticvoidmain(String[] args){// 推荐使用 try-with-resources 自动关闭流(Java 7+)try(BufferedReader br =newBufferedReader(newFileReader("demo.txt"))){String line;// 核心:readLine() 按行读取,一行一个字符串while((line = br.readLine())!=null){System.out.println("读取到内容:"+ line);}}catch(IOException e){// 必须处理异常:打印异常信息或做业务兜底System.err.println("读取文件失败:"+ e.getMessage()); e.printStackTrace();}}}
适用场景
  • 高效读取大文本文件(如日志文件、配置文件、CSV 文件);
  • 需要按行处理文本内容的场景(如解析行式数据、统计文件行数);
  • 控制台输入读取(new BufferedReader(new InputStreamReader(System.in)))。

2. BufferedWriter(字符缓冲输出流)

核心功能
  • 包装底层字符输出流(如 FileWriter),通过缓冲区减少写入次数,提升文本写入效率;
  • 提供 newLine() 方法,自动适配不同操作系统的换行符(Windows 是 \r\n,Linux 是 \n);
  • 仅支持字符/字符串的写入(write() 方法),无格式化能力,所有操作抛出 IOException
代码示例(高效写入文本文件)
importjava.io.BufferedWriter;importjava.io.FileWriter;importjava.io.IOException;publicclassBufferedWriterDemo{publicstaticvoidmain(String[] args){try(BufferedWriter bw =newBufferedWriter(newFileWriter("output.txt"))){// 写入字符串 bw.write("Java IO 缓冲流示例");// 换行(适配系统) bw.newLine();// 写入字符数组char[] chars ="BufferedWriter 高效写入".toCharArray(); bw.write(chars);// 手动刷新缓冲区(确保数据写入磁盘,close() 也会自动刷新) bw.flush();}catch(IOException e){System.err.println("写入文件失败:"+ e.getMessage()); e.printStackTrace();}}}
适用场景
  • 高频次写入文本数据(如批量生成文本文件、写入大量日志片段);
  • 对写入性能有要求,且仅需基础字符串/字符写入的场景;
  • 需要适配多系统换行符的跨平台文本写入。

3. PrintStream(字节打印流)

核心功能
  • 处理字节流的格式化输出工具,底层基于字节操作;
  • 支持 print()/println()/printf() 方法,可直接打印所有基本数据类型(int、double 等)和对象(自动调用 toString());
  • 屏蔽 IO 异常(内部捕获,不向外抛出),可通过 checkError() 方法判断是否写入失败;
  • 支持自动刷新(构造时指定 autoFlush=true),System.out 本质就是 PrintStream(控制台输出的核心实现)。
代码示例(控制台/文件输出)
importjava.io.FileOutputStream;importjava.io.PrintStream;publicclassPrintStreamDemo{publicstaticvoidmain(String[] args){// 1. 控制台输出(System.out 是 PrintStream 实例)PrintStream consoleOut =System.out; consoleOut.println("打印整数:"+123); consoleOut.printf("格式化输出:姓名=%s,年龄=%d\n","张三",25);// 2. 写入文件(字节流方式)try(PrintStream fileOut =newPrintStream(newFileOutputStream("printstream.txt"),true)){// autoFlush=true:调用 println/printf 时自动刷新缓冲区 fileOut.println("打印布尔值:"+true); fileOut.printf("浮点格式化:%.2f\n",3.1415);// 检查是否有错误if(fileOut.checkError()){System.err.println("写入文件出错");}}// 无需处理 IO 异常(PrintStream 内部捕获)}}
适用场景
  • 控制台输出(System.out/System.err);
  • 字节流场景下的格式化输出(如网络字节流、二进制文件中嵌入文本);
  • 不想处理 IO 异常,追求快速输出的简单场景。

4. PrintWriter(字符打印流)

核心功能
  • 处理字符流的格式化输出工具,是 PrintStream 的“字符版”,更适合文本处理;
  • 功能与 PrintStream 几乎一致:支持 print/println/printf、屏蔽 IO 异常、自动刷新;
  • 可直接包装字符流(如 FileWriter),也可包装字节流(底层自动转换),比 PrintStream 更适配文本场景。
代码示例(文本文件格式化输出)
importjava.io.PrintWriter;publicclassPrintWriterDemo{publicstaticvoidmain(String[] args){// try-with-resources 自动关闭流try(PrintWriter pw =newPrintWriter("printwriter.txt",true)){// autoFlush=true:println/printf 触发自动刷新 pw.println("打印对象:"+newObject());// 自动调用 toString() pw.printf("日期格式化:%tF %<tT\n",System.currentTimeMillis()); pw.print("null 处理:"); pw.println(null);// 打印字符串 "null",不抛空指针}// 无需处理 IO 异常}}
适用场景
  • 文本文件的格式化输出(日志生成、报表打印);
  • 字符流场景下的业务输出(如 Web 响应流、字符型网络通信);
  • 追求易用性,需要快速打印多种数据类型的场景(替代 BufferedWriter + 手动类型转换)。

四、核心区别

1. 缓冲流 vs 打印流(核心阵营差异)

  • 异常处理:缓冲流(BufferedReader/BufferedWriter)所有操作抛出 IOException,必须手动处理;打印流(PrintStream/PrintWriter)内部捕获异常,不向外抛出,仅通过 checkError() 检查。
  • 功能侧重:缓冲流仅专注“高效读写字符”,无格式化能力,仅支持字符/字符串操作;打印流侧重“格式化输出”,支持所有数据类型,提供 println/printf 等便捷方法。
  • 底层类型:缓冲流仅处理字符流,必须包装 Reader/Writer 子类;PrintStream 处理字节流,PrintWriter 处理字符流(可兼容字节流)。

2. PrintStream vs PrintWriter(打印流内部差异)

  • 底层操作:PrintStream 基于字节,PrintWriter 基于字符;
  • 文本适配:PrintWriter 更适合纯文本处理(换行、字符编码适配更友好),PrintStream 更适合字节流场景(如控制台、二进制文件);
  • 编码处理:PrintWriter 构造时可直接指定字符编码,PrintStream 需通过 OutputStreamWriter 间接指定,文本场景下 PrintWriter 更易用。

3. BufferedReader vs BufferedWriter(缓冲流内部差异)

  • 读写方向:BufferedReader 是输入流(读数据),核心方法 readLine();BufferedWriter 是输出流(写数据),核心方法 write()/newLine();
  • 操作逻辑:两者均依赖缓冲区,Reader 是“读入缓冲区再处理”,Writer 是“写入缓冲区再刷盘”,都需手动 flush(或 close)确保数据生效。

五、拓展技巧 & 最佳实践

1. 流的关闭:优先使用 try-with-resources

Java 7+ 提供的 try-with-resources 语法可自动关闭实现 AutoCloseable 接口的流,无需手动调用 close(),避免资源泄漏。所有 4 个类都支持该语法,是生产环境的首选。

2. 性能优化:缓冲流的缓冲区大小

默认情况下,BufferedReader/BufferedWriter 的缓冲区是 8192 字符(8KB),若处理超大文件,可手动指定更大的缓冲区(如 64KB)提升性能:

// 手动指定缓冲区大小为 65536 字符(64KB)BufferedReader br =newBufferedReader(newFileReader("bigfile.txt"),65536);

3. 异常兜底:打印流的错误检查

PrintStream/PrintWriter 不抛异常,但需通过 checkError() 检查写入是否失败,避免“写入失败但程序无感知”:

PrintWriter pw =newPrintWriter("test.txt"); pw.println("测试数据");if(pw.checkError()){thrownewRuntimeException("写入文件失败");// 手动抛出业务异常兜底} pw.close();

4. 选型口诀(快速决策)

  • 读文本、要高效、按行读 → 用 BufferedReader;
  • 写文本、要高效、纯字符 → 用 BufferedWriter;
  • 打日志、控制台、格式化、不想处理异常 → 用 PrintWriter(文本)/PrintStream(字节);
  • 跨平台换行、高频写 → 优先 BufferedWriter + newLine();
  • 多数据类型输出、快速开发 → 优先 PrintWriter。

六、总结

  1. BufferedReader/BufferedWriter 是“底层高效读写工具”,偏性能、需处理异常,适合对 IO 效率有要求的文本读写场景;
  2. PrintStream/PrintWriter 是“上层易用打印工具”,偏便捷、屏蔽异常,适合格式化输出、日志打印、控制台展示等场景;
  3. 文本处理优先选字符流(BufferedReader/BufferedWriter/PrintWriter),字节流场景(如控制台、二进制文件)选 PrintStream;
  4. 实际开发中,常组合使用(如 BufferedReader 读数据 + PrintWriter 格式化输出),兼顾效率与易用性。

Read more

Kotlin程序员面试算法宝典【1.7】

Kotlin程序员面试算法宝典【1.7】

3.3 如何从顶部开始逐层打印二叉树结点数据 【出自 WR 面试题】 难度系数:★★★☆☆ 题目描述:被考察系数:★★★★☆ 给定一棵二叉树,要求逐层打印二叉树结点的数据,例如有如下二叉树: 对这棵二叉树层序遍历的结果为 1, 2, 3, 4, 5, 6, 7。 分析与解答: 为了实现对二叉树的层序遍历,就要求在遍历一个结点的同时记录下它的孩子结点的信息,然后按照这个记录的顺序来访问结点的数据,在实现的时候可以采用队列来存储当前遍历到的结点的孩子结点,从而实现二叉树的层序遍历,遍历过程如下图所示: 在上图中,图( 1)首先把根结点 1 放到队列里面,然后开始遍历。图( 2)队列首元素(结点 1)出队列,同时它的孩子结点 2 和结点 3 进队列。图( 3)接着出队列的结点为 2,同时把它的孩子结点

By Ne0inhk
设计五种算法精确的身份证号匹配

设计五种算法精确的身份证号匹配

问题定义与数据准备 我们有两个Excel文件: * small.xlsx: 包含约5,000条记录。 * large.xlsx: 包含约140,000条记录。 目标:快速、高效地从large.xlsx中找出所有其“身份证号”字段存在于small.xlsx“身份证号”字段中的记录,并将这些匹配的记录保存到一个新的Excel文件result.xlsx中。 假设:身份证号字段名在两个表中都是id_card。 首先,我们进行准备工作,安装必要的库并模拟一些数据用于测试和性能估算。 pip install pandas openpyxl import pandas as pd import time import random # 为演示和测试,我们可以创建一些模拟数据(实际中使用pd.read_excel读取你的文件)defgenerate_id_card():"""

By Ne0inhk
【LeetCode经典题解】搞定二叉树最近公共祖先:递归法+栈存路径法,附代码实现

【LeetCode经典题解】搞定二叉树最近公共祖先:递归法+栈存路径法,附代码实现

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:Java.数据结构 【前言】 二叉树的最近公共祖先是数据结构中的经典问题,无论是算法面试还是实际开发都高频出现。本文将从问题本身出发,拆解递归法与栈存路径法两种核心思路的逻辑步骤,并附上完整代码实现,帮你快速掌握这一考点。 文章目录: * 一、二叉树的最近共同祖先 * 二、思路分析 * 方法一:递归法 * 判空 * 递归 * 结果 * 方法二:栈存路径法 * 获取路径 * 对齐栈长度 * 找公共祖先 * 三、代码展示 * 方法一:递归法 * 方法二:栈存路径法 * 四、总结 一、二叉树的最近共同祖先 二、思路分析 方法一:递归法 判空 * 如果根节点为空,直接返回null * 如果当前节点是p或者q,

By Ne0inhk
【强化学习】近端策略优化算法(PPO)万字详解(附代码)

【强化学习】近端策略优化算法(PPO)万字详解(附代码)

📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在👉强化学习专栏:        【强化学习】- 【单智能体强化学习】(9)---《近端策略优化算法(PPO)详解》 近端策略优化算法(PPO)详解 目录 PPO算法介绍 1. 背景 2. PPO 的核心思想 3. PPO 流程 4. 为什么 PPO 很强? 5. PPO 的直观类比 PPO算法的流程推导及数学公式 1. 背景与目标 2. PPO的概率比率 3. 优化目标 4. 值函数优化 5. 策略熵正则化

By Ne0inhk