说明:练习今日新学知识点——IO 字节缓冲流(
BufferedOutputStream/BufferedInputStream:内部缓冲区、flush()刷新)与接口化文件复制(CopyFile接口 +Copy01普通字节流实现 /BufferedCopy缓冲流实现):try-catch-finally 关闭资源、System.currentTimeMillis()耗时对比;以及字符流(Reader/Writer+FileReader/FileWriter:直接write(String)写中文无需getBytes()、字符流 = 字节流 + 字符集编码表、Charset字符集与乱码原理)与字符缓冲流(BufferedReader/BufferedWriter:readLine()逐行读取 /newLine()换行、读 → 处理 → 集合 → 排序 → 写回文件)。
难度:⭐⭐⭐
知识点:CopyFile 接口、FileInputStream / FileOutputStream、BufferedInputStream / BufferedOutputStream、byte[] 缓冲区、try-catch-finally、System.currentTimeMillis()
场景描述:课堂用 CopyFile 接口统一了"文件复制"的规范,并实现了两个版本:Copy01(普通字节流 + byte[8192] 数组)和 BufferedCopy(缓冲流包装底层流)。现在把两个实现整合进一个对比程序,复制同一个大文件,用耗时说话——验证"缓冲流更快"。
import java.io.*;
// ① 复制器接口:统一复制规范
public interface CopyFile {
String copy(String src, String desc) throws IOException;
}
// ② 普通字节流实现:FileInputStream + FileOutputStream + byte[8192] 数组 + try-catch-finally
class Copy01 implements CopyFile {
@Override
public String copy(String src, String desc) {
FileOutputStream fos = null;
FileInputStream fis = null;
try {
// TODO 1: 创建输出流 fos 和输入流 fis
// TODO 2: byte[8192] 缓冲区 + while 循环「读一批 → 写一批」
} catch (IOException e) {
return "文件复制失败";
} finally {
// TODO 3: finally 中关闭 fos / fis
// 提示:close() 也可能抛 IOException → 包装成 RuntimeException
}
return "文件复制成功";
}
}
// ③ 缓冲流实现:BufferedOutputStream / BufferedInputStream 包装底层流
class BufferedCopy implements CopyFile {
@Override
public String copy(String src, String desc) throws IOException {
// TODO 4: 先创建文件流,再用缓冲流包装它们
// TODO 5: byte[8192] 数组 + while ((count = bis.read(bytes)) != -1) { bos.write(bytes, 0, count); }
// TODO 6: 关闭:先关缓冲流再关底层流
return "复制成功";
}
}
// ④ 测试类:对比两种实现复制同一个大文件的耗时
public class CopyCompareTest {
public static void main(String[] args) throws IOException {
String src = "D:/大文件.zip"; // 换成你电脑上任意大于 10MB 的文件
// TODO 7: 用 new Copy01().copy(src, ...) 复制,System.currentTimeMillis() 统计耗时
// TODO 8: 用 new BufferedCopy().copy(src, ...) 复制,统计耗时
// TODO 9: 输出两次耗时,对比哪个更快
}
}
选做加分:
Copy01 改成"失败时返回提示信息"的 try-catch 风格,BufferedCopy 保持 throws 风格,体会两种异常处理方式思考题:
BufferedCopy 为什么通常比 Copy01 更快?(提示:缓冲流内部缓冲区减少与磁盘的交互次数)BufferedCopy 在缓冲流之上又用了 byte[8192] 数组,这层"双缓冲"是必须的吗?(提示:可以去掉外层数组试试看耗时变化)close() 也声明抛 IOException)难度:⭐⭐⭐
知识点:BufferedOutputStream、BufferedInputStream、flush()、Random 随机数、getBytes()、ASCII 码、数组统计
场景描述:课堂用 BufferedOutputStream 向文件写入了 100 万随机大写字母并演示了 flush()。现在把它做成一个完整闭环:写入 100 万随机字母 → 关闭流 → 再用 BufferedInputStream 读回 → 统计每个大写字母出现的次数 → 输出出现最多的字母。
import java.io.*;
import java.util.Random;
public class BufferedWriteReadTest {
public static void main(String[] args) throws IOException {
String path = "D:/letters.txt";
Random ran = new Random();
// ========== 写:100 万随机大写字母,每 500 个换行 + flush ==========
// TODO 1: 创建 FileOutputStream + BufferedOutputStream 包装它
// TODO 2: for 循环 100 万次,ran.nextInt(26) + 65 生成 A~Z,bos.write(c) 先写入内部缓冲区
// TODO 3: 每 500 个写一次 "\r\n".getBytes(),并调用 bos.flush() 刷新到磁盘
// TODO 4: 关闭缓冲流和文件流
// ========== 读:BufferedInputStream 逐字节读回并统计 ==========
// TODO 5: 创建 FileInputStream + BufferedInputStream 包装它
// TODO 6: int[] count = new int[26]; 统计每个大写字母出现次数
// while ((b = bis.read()) != -1) { if (b >= 'A' && b <= 'Z') count[b - 'A']++; }
// TODO 7: 遍历 count 输出每个字母出现次数;找出出现最多的字母
// TODO 8: 关闭流
}
}
输出示例:
字母出现统计:
A: 38462
B: 38610
...
Q: 38911 ← 出现最多的字母
选做加分:
Character.isLetterOrDigit() 判断\r\n 出现的次数,验证是否符合预期(约 1000000 / 500 ≈ 2000 次)close() 会自动 flush)思考题:
flush() 时,数据什么时候才会真正写入磁盘?(提示:缓冲区满 8KB 或 close() 时自动刷)bis.read() 逐字节读取也很快?(提示:数据预先成批读入内部缓冲区,读取其实发生在内存)read() 返回 -1 表示什么?(char) b 强转后为什么能显示字母?(提示:EOF;读到的字节值是 ASCII 码)ran.nextInt(26) + 65 为什么能生成 A~Z?(提示:65~90 正好对应字符 'A'~'Z')难度:⭐⭐⭐
知识点:Reader / Writer 抽象父类、FileReader / FileWriter、char[] 字符缓冲区、write(String) 直接写中文、字符流 vs 字节流
场景描述:课堂学了 FileReader / FileWriter:字符流 = 字节流 + 字符集编码表,写中文不用 getBytes()。现在用字符流写一个"中文复制器":FileWriter 写一段中文 → FileReader 读回并显示到控制台,实现"字符流三步走"的完整闭环。
import java.io.*;
public class CharStreamTest {
public static void main(String[] args) throws IOException {
// ========== 写:FileWriter 直接写中文(无需 getBytes) ==========
// TODO 1: new FileWriter("D:/中文.txt") 创建文件字符输出流
// TODO 2: fw.write("床前明月光,疑是地上霜。"); 直接写中文字符串
// fw.write("\r\n");
// fw.write("举头望明月,低头思故乡。");
// TODO 3: fw.flush(); 刷新缓冲区(字符流自带缓冲区)
// TODO 4: fw.close(); 关闭流
// ========== 读:FileReader 用 char[1024] 缓冲区读回 ==========
// TODO 5: new FileReader("D:/中文.txt") 创建文件字符输入流
// TODO 6: char[] chars = new char[1024];
// while ((count = fr.read(chars)) != -1) {
// System.out.print(new String(chars, 0, count)); // 字符数组 → 字符串
// }
// TODO 7: fr.close(); 关闭流
}
}
选做加分:
CopyFile 接口的第三个版本 CharCopy:用 FileReader + FileWriter + char[1024] 复制含中文的 txt 文件,对比与字节流版本(Copy01)复制中文文件的差异BufferedReader / BufferedWriter 包装字符流后复制,体会"字符流 + 字符缓冲流"的组合,BufferedReader.readLine() 一次读一行思考题:
fw.write("床前明月光") 不用像字节流那样先 getBytes()?(提示:字符流内部按字符集编码表完成"字符 → 字节"转换)fr.read(chars) 返回的是什么?(提示:本次读到的有效字符个数;-1 表示读完)new String(chars, 0, count) 的作用是什么?(提示:只取本次有效部分,避免把缓冲区残留脏数据也转进去)难度:⭐⭐⭐⭐
知识点:Charset 字符集、FileWriter(String, Charset) / FileReader(String, Charset) 指定编码、String 编码 getBytes(Charset) / 解码 new String(byte[], Charset)、乱码原理
场景描述:课堂讲了"字符流 = 字节流 + 字符集编码表",乱码的根源是读写字符集不一致。现在做个实验:用 GBK 字符集写入中文,再用默认字符集(UTF-8)读取 → 观察乱码;改用 GBK 字符集读取 → 中文正常显示。
import java.io.*;
import java.nio.charset.Charset;
public class CharsetTest {
public static void main(String[] args) throws IOException {
// ========== 写:用 GBK 字符集写入中文 ==========
// TODO 1: new FileWriter("D:/编码.txt", Charset.forName("GBK"))
// TODO 2: fw.write("中华人民共和国"); 直接写中文
// TODO 3: fw.close();
// ========== 读一:默认字符集(UTF-8)读 → 预期乱码 ==========
// TODO 4: new FileReader("D:/编码.txt") 默认字符集读取
// TODO 5: char[1024] 读回显示,观察是否出现乱码
// ========== 读二:指定 GBK 字符集读 → 预期正常 ==========
// TODO 6: new FileReader("D:/编码.txt", Charset.forName("GBK"))
// TODO 7: 再次读回显示,观察中文是否恢复正常
}
}
输出示例:
默认字符集(UTF-8)读取:�й????????? ← 乱码
指定字符集(GBK)读取:中华人民共和国 ← 正常
选做加分:
"中国".getBytes(Charset.forName("GBK")) 转成字节数组,再用 new String(bytes, Charset.forName("UTF-8")) 解码,观察输出乱码getBytes("GBK") + 字节流 FileOutputStream 写文件,验证"字符流"与"字节流 + 手动编码"是等价的思考题:
FileWriter 不指定字符集时用的是哪个字符集?(提示:平台操作系统默认字符集)难度:⭐⭐⭐
知识点:Scanner 键盘录入、FileWriter 字符流写入、String 拼接、flush() 刷新、close() 关闭流释放资源
场景描述:程序中的数据只存在于内存,程序结束就丢失了。要实现永久化存储,就要把数据写入本地文件(磁盘)。本例实现一个简单的用户注册:从键盘录入用户名和密码,保存到 D:/user.txt 文件中——程序下次运行,文件里的数据依然存在。
将键盘录入的用户名和密码保存到本地,实现永久化存储。
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class UserRegister {
public static void main(String[] args) throws IOException {
// ========== 1、获取用户输入的用户名和密码 ==========
// TODO 1: 创建 Scanner 对象读取键盘输入
// TODO 2: 提示"请输入用户名:",sc.nextLine() 接收用户名
// TODO 3: 提示"请输入密码:",sc.nextLine() 接收密码
// ========== 2、将用户名和密码写入到本地文件中 ==========
// TODO 4: new FileWriter("D:/user.txt") 创建文件字符输出流
// TODO 5: fw.write("用户名:" + username); fw.write("\r\n");
// fw.write("密码:" + password);
// TODO 6: fw.flush(); 把缓冲区内容刷入文件
// ========== 3、关流,释放资源 ==========
// TODO 7: fw.close(); 关闭流
System.out.println("注册成功!信息已保存到 D:/user.txt");
}
}
输出示例:
请输入用户名:admin
请输入密码:123456
注册成功!信息已保存到 D:/user.txt
(程序运行后打开 D:/user.txt 查看内容)
用户名:admin
密码:123456
选做加分:
FileReader 读取文件,校验输入的用户名密码与文件中的是否匹配new FileWriter("D:/user.txt", true) 实现多用户注册:一次运行保存一组,再次运行追加一组,不覆盖之前的数据BufferedWriter 包装后写入,体会"字符流 + 字符缓冲流"的组合写法思考题:
flush()?不写会怎样?(提示:字符流自带缓冲区,数据先到缓冲区,缓冲区满或 close() 时才落盘)FileWriter 换成 FileOutputStream,写之前需要做什么?(提示:字符串要先 getBytes() 转成字节数组)append=true 追加写有什么区别?(提示:默认每次运行会覆盖原文件内容)难度:⭐⭐⭐⭐
知识点:BufferedReader / BufferedWriter、readLine() 逐行读取 / newLine() 写入、String.split() 拆分、ArrayList 集合、Collections.sort() + Comparator 排序、Integer.parseInt() 字符串转整数
场景描述:D:/scores.txt 中保存了 6 名学生的成绩,格式为"姓名-成绩",顺序是乱的。用 BufferedReader 逐行读取 → split("-") 拆分成姓名和成绩 → 封装成 Student 对象存入 ArrayList → 按成绩从高到低排序 → 用 BufferedWriter 把带名次的排序结果写回 D:/sorted.txt——把"读文件 → 处理 → 集合 → 排序 → 写文件"串成一个完整闭环。
使用字符缓冲流读取文件中的数据,排序后再次写到本地文件。
D:/scores.txt(顺序是乱的)李四-78
王五-92
张三-66
赵六-85
孙七-95
周八-88
import java.io.*;
import java.util.*;
// 学生类:姓名 + 成绩
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 class ScoreSort {
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, new Comparator<Student>() { ... });
// 按成绩【降序】:o2.getScore() - o1.getScore()
// ========== 5、将排序后的集合中的数据写入到文件中 ==========
// TODO 7: new BufferedWriter(new FileWriter("D:/sorted.txt"))
// TODO 8: for 遍历排序后的集合,bw.write("第" + (i+1) + "名:" + 学生) + bw.newLine()
// TODO 9: bw.flush(); bw.close(); br.close();
System.out.println("排序完成!结果已写入 D:/sorted.txt");
}
}
输出示例:
排序完成!结果已写入 D:/sorted.txt
(程序运行后打开 D:/sorted.txt)
第1名:孙七-95
第2名:王五-92
第3名:周八-88
第4名:赵六-85
第5名:李四-78
第6名:张三-66
选做加分:
o1.getName().compareTo(o2.getName()))TreeSet 自动排序:让 Student implements Comparable<Student>(compareTo 按成绩降序),new TreeSet<>(list) 自动排序去重if (score < 0 || score > 100) continue;)思考题:
readLine() 返回 null 表示什么?while ((line = br.readLine()) != null) 循环靠什么终止?(提示:读到文件末尾返回 null)line.split("-") 拆分后返回什么类型?如果某一行没有 "-" 会怎样?(提示:返回 String[];不含分隔符则返回整行组成的长度 1 数组)Integer.parseInt(parts[1]) 的作用是什么?不转换直接用 parts[1] 排序,比较的是数字还是字符串?(提示:字符串比较按字典序,"95" 会排在 "9" 之后)newLine() 而不是手写 "\r\n"?(提示:跨平台行分隔符,Windows \r\n / Linux \n)flush() 或 close(),排序结果能立刻出现在文件中吗?(提示:字符缓冲流有缓冲区,不刷新不落盘)// Copy01.copy 核心(TODO 1~3):普通字节流 + byte[8192] 数组 + try-catch-finally
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream(desc);
fis = new FileInputStream(src);
byte[] bytes = new byte[8192];
int byteCount;
while ((byteCount = fis.read(bytes)) != -1) { // 读一批,-1 表示读完
fos.write(bytes, 0, byteCount); // 只写本次有效字节
}
} catch (IOException e) {
return "文件复制失败";
} finally {
try {
fos.close();
fis.close();
} catch (IOException e) {
throw new RuntimeException(e); // close 异常包装成运行时异常
}
}
return "文件复制成功";
// BufferedCopy.copy 核心(TODO 4~6):缓冲流包装底层流 + 双缓冲
FileOutputStream fos = new FileOutputStream(desc);
BufferedOutputStream bos = new BufferedOutputStream(fos); // 缓冲输出流包装文件输出流
FileInputStream fis = new FileInputStream(src);
BufferedInputStream bis = new BufferedInputStream(fis); // 缓冲输入流包装文件输入流
byte[] bytes = new byte[8192];
int count;
while ((count = bis.read(bytes)) != -1) {
bos.write(bytes, 0, count);
}
bis.close(); // 先关缓冲流
fis.close();
bos.close();
fos.close();
return "复制成功";
// CopyCompareTest.main 核心(TODO 7~9):两次复制 + 两次计时对比
String src = "D:/大文件.zip";
long start = System.currentTimeMillis();
new Copy01().copy(src, "D:/copy1.zip");
long mid = System.currentTimeMillis();
new BufferedCopy().copy(src, "D:/copy2.zip");
long end = System.currentTimeMillis();
System.out.println("普通字节流耗时:" + (mid - start) + " 毫秒");
System.out.println("缓冲流耗时:" + (end - mid) + " 毫秒");
// 写(TODO 1~4):100 万随机大写字母 + 每 500 个换行 + flush
FileOutputStream fos = new FileOutputStream("D:/letters.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
Random ran = new Random();
for (int i = 0; i < 1000000; i++) {
int c = ran.nextInt(26) + 65; // 65~90 → 'A'~'Z'
bos.write(c); // 先写入内部缓冲区
if (i % 500 == 0) {
bos.write("\r\n".getBytes()); // 每 500 个字母换一行
bos.flush(); // 手动把缓冲区数据刷到磁盘
}
}
bos.close();
fos.close();
// 读 + 统计(TODO 5~8):逐字节读回,统计每个大写字母出现次数
FileInputStream fis = new FileInputStream("D:/letters.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
int[] count = new int[26]; // 下标 0~25 对应 'A'~'Z'
int b;
while ((b = bis.read()) != -1) {
if (b >= 'A' && b <= 'Z') { // 过滤换行符等非字母字符
count[b - 'A']++;
}
}
bis.close();
fis.close();
int max = 0;
char maxChar = 'A';
for (int i = 0; i < count.length; i++) {
System.out.println((char) ('A' + i) + ": " + count[i]);
if (count[i] > max) {
max = count[i];
maxChar = (char) ('A' + i);
}
}
System.out.println("出现最多的字母:" + maxChar + ",共 " + max + " 次");
// 写(TODO 1~4):FileWriter 直接写中文,无需 getBytes
FileWriter fw = new FileWriter("D:/中文.txt");
fw.write("床前明月光,疑是地上霜。");
fw.write("\r\n");
fw.write("举头望明月,低头思故乡。");
fw.flush(); // 字符流自带缓冲区,flush 刷新到文件
fw.close();
// 读(TODO 5~7):FileReader + char[1024] 批量读取,字符数组 → 字符串
FileReader fr = new FileReader("D:/中文.txt");
char[] chars = new char[1024];
int count;
while ((count = fr.read(chars)) != -1) {
System.out.print(new String(chars, 0, count)); // 只取本次有效部分
}
fr.close();
// 选做:CopyFile 接口的字符流实现(char[1024] 复制中文 txt)
class CharCopy implements CopyFile {
@Override
public String copy(String src, String desc) throws IOException {
FileReader fr = new FileReader(src);
FileWriter fw = new FileWriter(desc);
char[] chars = new char[1024];
int count;
while ((count = fr.read(chars)) != -1) {
fw.write(chars, 0, count);
}
fw.close();
fr.close();
return "复制成功";
}
}
// 写(TODO 1~3):指定 GBK 字符集写入中文
FileWriter fw = new FileWriter("D:/编码.txt", Charset.forName("GBK"));
fw.write("中华人民共和国");
fw.close();
// 读一(TODO 4~5):默认字符集(UTF-8)读 → 乱码
FileReader fr1 = new FileReader("D:/编码.txt");
char[] chars = new char[1024];
int count;
while ((count = fr1.read(chars)) != -1) {
System.out.println("默认字符集(UTF-8)读取:" + new String(chars, 0, count)); // 乱码
}
fr1.close();
// 读二(TODO 6~7):指定 GBK 字符集读 → 正常
FileReader fr2 = new FileReader("D:/编码.txt", Charset.forName("GBK"));
while ((count = fr2.read(chars)) != -1) {
System.out.println("指定字符集(GBK)读取:" + new String(chars, 0, count)); // 正常
}
fr2.close();
// 选做:String 编码/解码乱码演示(getBytes 编码 + new String 解码)
byte[] gbkBytes = "中国".getBytes(Charset.forName("GBK"));
String wrong = new String(gbkBytes, Charset.forName("UTF-8"));
System.out.println(wrong); // 输出乱码(编码表不一致)
String right = new String(gbkBytes, Charset.forName("GBK"));
System.out.println(right); // 输出:中国
// 1、获取用户输入的用户名和密码(TODO 1~3)
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
// 2、将用户名和密码写入到本地文件中(TODO 4~6)
FileWriter fw = new FileWriter("D:/user.txt"); // 创建文件字符输出流
fw.write("用户名:" + username); // 直接写字符串,无需 getBytes
fw.write("\r\n"); // 换行(Windows 是 \r\n)
fw.write("密码:" + password);
fw.flush(); // 刷新缓冲区,确保写入文件
// 3、关流,释放资源(TODO 7)
fw.close();
System.out.println("注册成功!信息已保存到 D:/user.txt");
// 选做一:追加模式多用户注册(不覆盖之前数据)
FileWriter fw = new FileWriter("D:/user.txt", true); // append=true 追加写
fw.write("用户名:" + username + "\r\n");
fw.write("密码:" + password + "\r\n");
fw.close();
// 选做二:FileReader 读取文件做登录校验
FileReader fr = new FileReader("D:/user.txt");
char[] chars = new char[1024];
int count;
String data = "";
while ((count = fr.read(chars)) != -1) {
data += new String(chars, 0, count); // 读出文件全部内容
}
fr.close();
// 判断 data 是否包含 "用户名:" + username + 换行 + "密码:" + password 片段
boolean ok = data.contains("用户名:" + username + "\r\n" + "密码:" + password);
System.out.println(ok ? "登录成功" : "用户名或密码错误");
// 1、将文件中的数据读取到程序中(TODO 1~2):BufferedReader 逐行读取
BufferedReader br = new BufferedReader(new FileReader("D:/scores.txt"));
ArrayList<Student> list = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
// 2、对读取到的数据进行处理(TODO 3~4):split 拆分 + 转整数
String[] parts = line.split("-");
String name = parts[0];
int score = Integer.parseInt(parts[1]);
// 3、将处理后的数据添加到集合中(TODO 5):封装 Student 对象
list.add(new Student(name, score));
}
br.close();
// 4、对集合中的数据进行排序(TODO 6):Comparator 按成绩降序
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getScore() - o1.getScore(); // 降序:交换参数
}
});
// 5、将排序后的集合中的数据写入到文件中(TODO 7~9)
BufferedWriter bw = new BufferedWriter(new FileWriter("D:/sorted.txt"));
for (int i = 0; i < list.size(); i++) {
bw.write("第" + (i + 1) + "名:" + list.get(i));
bw.newLine(); // 跨平台换行
}
bw.flush();
bw.close();
System.out.println("排序完成!结果已写入 D:/sorted.txt");
// 选做一:成绩相同时按姓名升序(Comparator 二次比较)
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int result = o2.getScore() - o1.getScore(); // 先比成绩【降序】
return result == 0 ? o1.getName().compareTo(o2.getName()) : result; // 成绩相同再比姓名【升序】
}
});
// 选做二:Student 实现 Comparable,TreeSet 自动排序
class Student implements Comparable<Student> {
// ...属性、构造、getter、toString 省略...
@Override
public int compareTo(Student o) {
int result = o.getScore() - this.getScore(); // 按成绩降序
return result == 0 ? this.getName().compareTo(o.getName()) : result;
}
}
TreeSet<Student> set = new TreeSet<>(list); // 自动排序 + 去重
| 知识点 | 说明 |
|---|---|
CopyFile 接口 |
统一文件复制规范,体现面向接口编程 / 多态 |
FileInputStream / FileOutputStream |
文件字节输入 / 输出流 |
BufferedInputStream / BufferedOutputStream |
缓冲流包装底层字节流,内部 8KB 缓冲区 |
| 缓冲流构造方法 | BufferedOutputStream(OutputStream out) / BufferedInputStream(InputStream in),参数是底层流 |
byte[] 缓冲区循环复制 |
while ((count = bis.read(bytes)) != -1) { bos.write(bytes, 0, count); } |
flush() 刷新 |
缓冲区未满时手动强制写入磁盘 |
| try-catch-finally | 流变量 null 声明 + finally 关闭 + IOException 包装 RuntimeException |
| 关闭顺序 | 先关缓冲流再关底层流(close 缓冲流会自动关闭底层流) |
System.currentTimeMillis() |
复制前后毫秒数相减,做耗时对比 |
Random 随机字母 |
ran.nextInt(26) + 65 生成 'A'~'Z'(65~90 是 ASCII 码) |
read() 返回 -1 |
EOF 结束标记;(char) b 强转把字节值显示为字母 |
| 数组统计 | count[b - 'A']++ 用下标映射字母,统计频次并找出最大值 |
FileReader / FileWriter |
文件字符输入 / 输出流;字符流 = 字节流 + 字符集编码表 |
write(String) 直接写中文 |
字符流可直接写字符串,无需 getBytes() 转换 |
char[] 字符缓冲区 |
fr.read(chars) 批量读字符;new String(chars, 0, count) 转字符串 |
Charset 字符集 |
FileWriter(String, Charset) / FileReader(String, Charset) 构造指定编码 |
| String 编码 / 解码 | getBytes(Charset) 字符串 → 字节(编码);new String(byte[], Charset) 字节 → 字符串(解码) |
| 乱码原理 | 读写字符集不一致 → 乱码;GBK 汉字 2 字节,UTF-8 汉字 3 字节 |
| Scanner 键盘录入 | sc.nextLine() 获取用户名、密码输入(配合 FileWriter 完成数据持久化) |
| 数据永久化存储 | 内存数据程序结束即丢失;写入本地文件(磁盘)即可永久保存 |
FileWriter 追加模式 |
new FileWriter(String, true) append=true 追加写,不覆盖原文件内容 |
| 登录校验(选做) | FileReader 读文件 + String.contains() 判断用户名密码是否匹配 |
BufferedReader / BufferedWriter |
字符缓冲流包装 FileReader / FileWriter,提供高效读写 |
readLine() 逐行读取 |
读取一行(不含终止符),到文件末尾返回 null;while ((line = br.readLine()) != null) |
newLine() 写入换行 |
按系统属性写行分隔符(跨平台,替代手写 \r\n) |
String.split() 拆分 |
line.split("-") 按分隔符把一行拆成 String[],用于解析"姓名-成绩" |
Integer.parseInt() |
字符串 → 整数转换,排序时才能按数值比较 |
| 集合 + 排序 | ArrayList 存储 Student 对象 + Collections.sort + Comparator 匿名内部类按成绩降序 |
| 读 → 处理 → 集合 → 排序 → 写回 | 文件数据处理五步闭环:字符缓冲流读 → 处理 → 集合 → 排序 → 字符缓冲流写回 |