# 2026年8月7日 课后作业 — IO 缓冲流 + 字符流:中文读写、成绩排序写回、接口化文件复制 > **说明**:本次课程学习了 **IO 缓冲流**(`BufferedOutputStream` / `BufferedInputStream`:内部缓冲区、`flush()` 刷新、基础流 vs 扩展流)、**字符流**(`FileReader` / `FileWriter`:字符流 = 字节流 + 字符集编码表、直接 `write(String)` 写中文无需 `getBytes()`)、**字符缓冲流**(`BufferedReader` / `BufferedWriter`:`readLine()` 逐行读取、`newLine()` 跨平台换行)以及 **接口化文件复制**(`CopyFile` 接口 + 面向接口编程 / 多态 + `System.currentTimeMillis()` 耗时对比)。 > > 本作业共 **3 道题**:**基础题(必做)**——字符流中文读写器;**进阶题(必做)**——字符缓冲流成绩排序写回;**挑战题(选做)**——接口化文件复制效率对比器。覆盖今日 80% 以上知识点。 --- ## 📌 作业总体要求 ### 一、命名要求 | 项目 | 要求 | |------|------| | **类名** | `Demo_题号_你的姓名`(如 `Demo_01_张三`、`Demo_02_李四`、`Demo_03_王五`) | | **文件头注释** | 每个 Java 文件头部需包含 `@author`、`@title`、`@create` 文档注释 | ### 二、提交方式与截止时间 | 项目 | 要求 | |------|------| | **提交方式** | 将源码文件 + 运行结果截图提交至班级指定平台 | | **截止时间** | 下次上课前 | --- ## 一、基础题(必做) ### 题目 1:字符流中文读写器 CharFileDemo(⭐⭐ 必做) **知识点**:字符流概念(字符流 = 字节流 + 字符集编码表)、`FileWriter`(直接 `write(String)` 写中文、六种写方法)、`FileReader`(`read()` / `read(char[])` 两种读法)、`new String(chars, 0, count)` 字符数组转字符串、`flush()` / `close()` **场景描述**:字节流操作中文很不方便,需要 `getBytes()` 手动转字节数组。字符流内部按**字符集编码表**完成"字符 ↔ 字节"转换,可以直接 `write("中文")`。请用 `FileWriter` 把一首中文古诗写入 `D:/poem.txt`,再用 `FileReader` 读回并显示到控制台。 **题目要求**: ```java import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /** * @author 你的姓名 * @title 题目1:字符流中文读写器 * @create 2026/8/7 */ public class Demo_01_姓名 { public static void main(String[] args) throws IOException { // ========== 写:FileWriter 直接写中文(无需 getBytes) ========== // TODO 1: new FileWriter("D:/poem.txt") 创建文件字符输出流 // TODO 2: 用 fw.write("...") 写至少 2 句中文(如静夜思),句末用 fw.write("\r\n") 换行 // TODO 3: fw.flush(); // 字符流自带缓冲区,强制刷新到文件 // TODO 4: fw.close(); // 关闭流 // ========== 读:FileReader 读回并显示 ========== // TODO 5: new FileReader("D:/poem.txt") 创建文件字符输入流 // TODO 6: char[] chars = new char[1024]; // int count; // while ((count = fr.read(chars)) != -1) { // System.out.print(new String(chars, 0, count)); // } // TODO 7: fr.close(); // 关闭流 } } ``` **预期输出示例**: ``` 床前明月光,疑是地上霜。 举头望明月,低头思故乡。 ``` **要求说明**: 1. 必须使用**字符流**(`FileWriter` / `FileReader`),不允许用字节流 2. 写中文**不需要**调用 `getBytes()`,直接 `write("中文")` 3. 读取使用 `read(char[])` 批量读取 + `new String(chars, 0, count)`,不允许逐字符打印 **💡 提示**: - `FileWriter` / `FileReader` 构造方法**只传路径**即可(使用系统默认字符集) - `fr.read(chars)` 返回**本次读到的有效字符个数**,可能小于 `chars.length`,所以转字符串时务必用 `new String(chars, 0, count)` 截断有效部分 - `flush()` 之后还可以继续写;`close()` 之前会自动刷新一次 **🎯 挑战加分(选做)**: 1. 用 `new FileWriter("D:/poem.txt", true)` 追加写第二段,验证 `append=true` 追加 vs 默认覆盖 2. 用 `write(String, off, len)` 只写入诗句的某一段子串 3. 统计读到的**汉字个数**(提示:读到的 char 值 > 127 即为非 ASCII 中文字符) --- ## 二、进阶题(必做) ### 题目 2:字符缓冲流成绩排序写回 ScoreSort(⭐⭐⭐ 必做) **知识点**:`BufferedReader` / `BufferedWriter`、`readLine()` 逐行读取(末尾返回 null)、`newLine()` 跨平台换行、`String.split("-")` 拆分、`Integer.parseInt()` 字符串转整数、`ArrayList` + `Collections.sort` + `Comparator`(Lambda)排序、读取→处理→集合→排序→写回五步闭环 **场景描述**:`D:/scores.txt` 中保存了若干行"姓名-成绩"(顺序是乱的),如下: ``` 李四-78 王五-92 张三-66 赵六-85 孙七-95 周八-88 ``` 请用 `BufferedReader` 逐行读取 → `split("-")` 拆分成姓名和成绩 → 封装 `Student` 对象存入 `ArrayList` → 按成绩**从高到低**排序 → 用 `BufferedWriter` 把带名次的结果写回 `D:/sorted.txt`。 **题目要求**: ```java import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; /** * @author 你的姓名 * @title 题目2:字符缓冲流成绩排序写回 * @create 2026/8/7 */ public class Demo_02_姓名 { // 学生类:姓名 + 成绩 static class Student { private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } @Override public String toString() { return name + "-" + score; } } public static void main(String[] args) throws IOException { // ========== 1、将文件中的数据读取到程序中 ========== // TODO 1: new BufferedReader(new FileReader("D:/scores.txt")) 字符缓冲流包装 // TODO 2: while ((line = br.readLine()) != null) { ... } 逐行读取 // ========== 2、对读取到的数据进行处理 ========== // TODO 3: String[] parts = line.split("-"); 按 "-" 拆分成 [姓名, 成绩] // TODO 4: int score = Integer.parseInt(parts[1]); 成绩字符串 → 整数 // ========== 3、将处理后的数据添加到集合中 ========== // TODO 5: new Student(parts[0], score) 封装对象,list.add(stu) 存入 ArrayList // ========== 4、对集合中的数据进行排序 ========== // TODO 6: Collections.sort(list, (o1, o2) -> o2.getScore() - o1.getScore()); // 按成绩降序 // ========== 5、将排序后的集合中的数据写入到文件中 ========== // TODO 7: new BufferedWriter(new FileWriter("D:/sorted.txt")) // TODO 8: for 遍历排序后的集合,bw.write("第" + (i+1) + "名:" + list.get(i)) + bw.newLine() // TODO 9: bw.flush(); bw.close(); br.close(); System.out.println("排序完成!结果已写入 D:/sorted.txt"); } } ``` **预期输出示例**(`D:/sorted.txt` 内容): ``` 第1名:孙七-95 第2名:王五-92 第3名:周八-88 第4名:赵六-85 第5名:李四-78 第6名:张三-66 ``` **要求说明**: 1. 必须使用**字符缓冲流**(`BufferedReader` / `BufferedWriter`),不允许用普通字符流 2. 读取用 `readLine()` 逐行读取,`while ((line = br.readLine()) != null)` 循环 3. 排序用 `Collections.sort` + `Comparator`(Lambda 或匿名内部类均可),**按成绩降序** 4. 写回用 `newLine()` 换行(不要手写 `"\r\n"`) **💡 提示**: - `line.split("-")` 返回 `String[]`,`parts[0]` 是姓名、`parts[1]` 是成绩字符串 - `Integer.parseInt(parts[1])` 把成绩字符串转成整数后,才能按**数值**降序(否则字符串按字典序,"95" 会排在 "9" 后面导致排序错误) - `bw.write("第" + (i + 1) + "名:" + list.get(i))` 直接拼接对象会自动调用重写的 `toString()` 输出"姓名-成绩" **🎯 挑战加分(选做)**: 1. **二次排序**:成绩相同时按姓名**字典序升序**(`result == 0 ? o1.getName().compareTo(o2.getName()) : result`) 2. 让 `Student implements Comparable`,用 `TreeSet` 自动排序去重 3. **脏数据过滤**:读取时跳过成绩不在 0~100 范围的行 --- ## 三、挑战题(选做) ### 题目 3:接口化文件复制效率对比器 CopyCompare(⭐⭐⭐⭐ 选做) **知识点**:`CopyFile` 接口(面向接口编程 / 多态)、`FileInputStream` / `FileOutputStream` 普通字节流、`BufferedInputStream` / `BufferedOutputStream` 字节缓冲流、`byte[]` 缓冲区循环复制、try-catch-finally 关闭资源、`System.currentTimeMillis()` 耗时对比、基础流 vs 扩展流 **场景描述**:课堂用 `CopyFile` 接口统一了文件复制规范,并实现了两个版本:`Copy01`(普通字节流 + `byte[8192]` 数组)和 `BufferedCopy`(缓冲流包装底层流)。请定义 `CopyFile` 接口 + 实现这两个版本,复制同一个大文件,用耗时说话——**验证"缓冲流更快"**。 **题目要求**: ```java import java.io.*; /** * @author 你的姓名 * @title 题目3:接口化文件复制效率对比器 * @create 2026/8/7 */ public class Demo_03_姓名 { // ========== ① 复制器接口:统一复制规范 ========== interface CopyFile { String copy(String src, String desc) throws IOException; } // ========== ② 普通字节流实现:FileInputStream + FileOutputStream + byte[8192] + try-catch-finally ========== static class Copy01 implements CopyFile { @Override public String copy(String src, String desc) { // TODO 1: FileOutputStream fos / FileInputStream fis 声明为 null,try 中创建 // TODO 2: byte[8192] 缓冲区 + while ((len = fis.read(bytes)) != -1) { fos.write(bytes, 0, len); } // TODO 3: catch (IOException e) 返回 "文件复制失败" // TODO 4: finally 中关闭 fos / fis(close 也可能抛 IOException → 包装成 RuntimeException) return "文件复制成功"; } } // ========== ③ 缓冲流实现:BufferedOutputStream / BufferedInputStream 包装底层流 ========== static class BufferedCopy implements CopyFile { @Override public String copy(String src, String desc) throws IOException { // TODO 5: 先创建文件流,再用缓冲流包装(new BufferedOutputStream(fos)) // TODO 6: byte[8192] + while ((count = bis.read(bytes)) != -1) { bos.write(bytes, 0, count); } // TODO 7: 关闭:先关缓冲流再关底层流 return "复制成功"; } } public static void main(String[] args) throws IOException { String src = "D:/大文件.zip"; // 换成你电脑上任意大于 10MB 的文件 // TODO 8: 用 new Copy01().copy(src, "D:/copy1.zip") 复制,System.currentTimeMillis() 统计耗时 // TODO 9: 用 new BufferedCopy().copy(src, "D:/copy2.zip") 复制,统计耗时 // TODO 10: 输出两次耗时,对比哪个更快 } } ``` **预期输出示例**: ``` 普通字节流耗时:486 毫秒 缓冲流耗时:203 毫秒 缓冲流更快! ``` **要求说明**: 1. `CopyFile` 接口方法 `String copy(String src, String desc) throws IOException` 2. 两个实现类都必须 `implements CopyFile`,体现**面向接口编程 / 多态** 3. 复制前后对比源文件与目标文件大小,必须完全一致 4. 用 `System.currentTimeMillis()` 分别统计两个版本的复制耗时并输出对比 **💡 提示**: - `Copy01` 用 try-catch-finally 内部处理异常(返回提示信息);`BufferedCopy` 可以直接 `throws IOException`(接口允许两种异常处理风格并存) - 关闭顺序:`Copy01` 先关 fos 再关 fis;`BufferedCopy` 先关缓冲流再关底层流 - 复制同一个大文件(视频、zip 等)效果更明显;小文件耗时差异很小 **🎯 挑战加分(选做)**: 1. **缓冲区大小对比**:把 `byte[]` 改成 1024 / 4096 / 8192 / 16384 各测一次,总结"缓冲区大小 → 耗时"规律 2. **字符集乱码实验**:用 `FileWriter` 的 `Charset.forName("GBK")` 写一个中文文件,再用默认字符集的 `FileReader` 读取,观察乱码;改用 `FileReader(path, Charset.forName("GBK"))` 指定同一字符集读,验证乱码消失 3. **IO 流体系梳理**:仿照课堂 Demo04 小结,画出今日 IO 流完整体系图(基础流:字节/字符 + 输入/输出;处理流:缓冲字节/缓冲字符),并说明每个流"处理什么类型、怎么配对包装" --- ## 四、提交要求 - **类名**:`Demo_题号_姓名`(如 `Demo_01_张三`、`Demo_02_李四`、`Demo_03_王五`) - **文件头注释**:每个 Java 文件头部必须包含 `@author`(你的姓名)、`@title`(题目名称)、`@create 2026/8/7` - **提交内容**:源码文件 + 运行结果截图 - **必做/选做**:基础题(题目 1)、进阶题(题目 2)为**必做**;挑战题(题目 3)为**选做**,鼓励学有余力的同学完成