2026年8月7日-作业.md 13 KB

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 读回并显示到控制台。

题目要求:

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。

题目要求:

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<Student>,用 TreeSet 自动排序去重
  3. 脏数据过滤:读取时跳过成绩不在 0~100 范围的行

三、挑战题(选做)

题目 3:接口化文件复制效率对比器 CopyCompare(⭐⭐⭐⭐ 选做)

知识点:CopyFile 接口(面向接口编程 / 多态)、FileInputStream / FileOutputStream 普通字节流、BufferedInputStream / BufferedOutputStream 字节缓冲流、byte[] 缓冲区循环复制、try-catch-finally 关闭资源、System.currentTimeMillis() 耗时对比、基础流 vs 扩展流

场景描述:课堂用 CopyFile 接口统一了文件复制规范,并实现了两个版本:Copy01(普通字节流 + byte[8192] 数组)和 BufferedCopy(缓冲流包装底层流)。请定义 CopyFile 接口 + 实现这两个版本,复制同一个大文件,用耗时说话——验证"缓冲流更快"。

题目要求:

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)为选做,鼓励学有余力的同学完成