介绍
借用台必须在应用关闭后仍记得每件工具由谁持有。这个独立环境提供了正确的借用规则和一份新借用台文件,其中包括已有借用和维修工具。当前不完整的存储代码在加载时丢失已保存的状态,也不会写入变更。
你将把 UTF-8 文件记录映射为对象,并且只保存合法变更。你应已掌握上一实验中的对象规则;无需那个环境中的任何文件。这是单进程教学应用,不是数据库,也不保证抵抗断电。阅读与练习时间为作者估计的 45 分钟。
加载磁盘上实际保存的状态
在本步骤中,你将阅读一条普通文件记录,再修复保存的状态和借用人关系转换为 Java 对象的方式。
打开 Terminal 标签页。cd 选择本独立实验的目录。可用 Ctrl+U 清空尚未执行的错误 shell 输入行。遇到文件路径错误时,回到此目录,再重试正确命令。
cd /home/labex/project/tool-library
应用提供了可用的对象规则,但存储功能不完整。先用一个小型练习文件观察五字段格式,避免影响已有借用。mkdir -p 在需要时创建练习目录。printf 按指定格式输出:\t 生成制表符,\n 结束记录;> 把输出写入指定文件,替换之前的练习副本。单引号防止 shell 改变格式。cp 把成员列表复制到该文件旁边,因为应用会在借用台输入文件旁查找 members.tsv。这些命令都不会改变真实借用台。
mkdir -p work
printf 'S01\tSpare drill\tAVAILABLE\t-\tPractice only\n' > work/sample.tsv
cp members.tsv work/members.tsv
编译当前源代码。-encoding UTF-8 指定文本编码,-d out 指定编译输出位置,*.java 选择当前目录的 Java 文件。只有编译成功且没有错误时才能继续。
javac -encoding UTF-8 -d out *.java
-cp out 告诉 Java 类所在的位置。-Dfile.encoding=UTF-8 选择运行时文本编码。练习输入是 work/sample.tsv;--memory 防止本次演示保存变更。
java -Dfile.encoding=UTF-8 -cp out Desk work/sample.tsv --memory
在程序内输入以下内容,每行后按 Enter:
list
borrow S01 M01
list
quit
S01 初始为 AVAILABLE,借用人为 -;借给 M01 会成功。即使加载器不完整,这个普通样例也能成功,因为练习行的状态恰好就是错误代码假定的状态。它不能证明已有借用得到保留。这个进程关闭时,练习中的变更就会消失。
读取真实借用台和登记成员。cat 显示文件而不编辑文件:
cat desk.tsv
cat members.tsv
五个以制表符分隔的字段依次为:ID、名称、枚举状态、借用人、备注。这个受控格式允许字段中包含空格和 UTF-8 文本,但不允许制表符或换行。T24 已由 M03 借用;T25 由 M02 借用,备注为 Cafe workshop。T23 正在维修。这些事实必须在加载后保留。不要直接编辑 desk.tsv、members.tsv 或 reference 目录。
阅读准备好的存储代码:
cat Store.java
Store 是持久化边界:load 把记录转换为对象;save 把对象转换为记录。Files.readAllLines 显式使用 UTF-8 读取。fields(line) 检查格式,得到 f[0] 到 f[4] 五个字段。找到 new Tool(...) 调用:它目前为每一行都代入 Tool.State.AVAILABLE 和 "-"。预测这会如何影响 T24 的借用人及 T23 的维修状态。循环、列表、解析和验证辅助方法已经准备好,请保留它们。无需重写其中的 HashSet 实现。
编辑前,创建只包含源代码的备份:
cp Store.java work/Store-before-load.java.txt
nano Store.java
和第一个实验一样,Ctrl+W 用于搜索,Enter 确认搜索,方向键移动光标,Backspace 修正字符。搜索 Tool tool = new Tool,定位这一次构造方法调用。编辑状态和借用人参数,让它变为:
Tool tool = new Tool(f[0], f[1], Tool.State.valueOf(f[2]), f[3], f[4]);
Tool.State.valueOf(f[2]) 把保存的枚举名称转换为对应枚举值;f[3] 恢复保存的借用人。保留 ID、名称、备注参数,以及周围的循环和所有辅助方法。你是在映射已有信息,而不是创建一件全新的可借工具。按 Ctrl+O、Enter 保存,再按 Ctrl+X 退出。放弃尚未保存的错误修改时,按 Ctrl+X、N。若有编译错误,重新打开文件并搜索这个调用来修复。如果确实需要重新开始本次编辑,cp work/Store-before-load.java.txt Store.java 只恢复这个源文件;之后重新完成映射。绝不要为了撤销编辑而重新运行 setup。
这里,throws IOException 声明文件访问可能失败。输入缺失或格式错误时,命令循环输出 Cannot continue 并停止。这属于文件问题,不是借用拒绝。修正指出的路径或源代码映射;不要清空或重写原记录来消除错误。
编译保存的源代码,再启动真实借用台。与演示不同,这里省略 --memory:下一步完成保存后,真实借用台会持久化合法变更。语法错误会指出文件和行号;在 nano 中修复,编译成功后再启动,不要运行旧的类。
javac -encoding UTF-8 -d out *.java
java -Dfile.encoding=UTF-8 -cp out Desk desk.tsv
在借用台程序内输入这些行:
list
members
quit
列表必须保留 T24 → M03、T25 → M02、T23 的 MAINTENANCE 状态及所有备注。members 显示登记 ID。打开和列出记录不得重写文件。quit 返回 shell。下一步完成真正的保存;单靠成功消息无法证明持久化。
保存合法变更并重新打开借用台
在本步骤中,你将让一次新借用和一次合法归还在真实进程退出后仍然保留。
从同一个 Terminal 标签页的 shell 开始。保存会序列化每个当前对象,包括未改动的借用和维修记录。row(tool) 用制表符连接五个字段。循环把所有行加入列表;Files.write 将这些行写为 UTF-8。只有领域方法正常返回后,命令循环才调用 save。被拒绝的操作不得写入数据文件,包括重写完全相同的内容。
阅读命令框架,把这条规则与存储边界联系起来:
cat Desk.java
找到 found.borrow(c[2])、found.giveBack(c[2]) 以及后面的 Store.save(path, tools)。抛出的领域异常会跳过保存调用,变为正常的拒绝。保留准备好的顺序;在拒绝处理代码中添加写入会违反约定。
创建只包含源代码的备份,保留已经修正的加载器:
cp Store.java work/Store-before-save.java.txt
nano Store.java
按 Ctrl+W,搜索 TODO: write all rows,再按 Enter。阅读它正上方的循环:该循环为每件工具收集一条序列化记录,包括未改动的借用。用方向键移到注释末尾,按 Backspace 删除注释字符。在相同缩进处输入写入语句。仅将 TODO 注释替换为下面的实际写入,放在循环之后、方法结束的花括号之前:
Files.write(path, rows, StandardCharsets.UTF_8);
path 是选定的借用台文件;rows 是完整的序列化列表,不只是最后改变的对象。显式指定字符集可保留所有字段的文本编码。保留已经修正的 load 方法、记录循环及所有辅助方法。按 Ctrl+O、Enter、Ctrl+X 保存并退出。对于尚未保存的错误,按 Ctrl+X、N 放弃修改。对于已保存的源代码错误,重新打开并修正;也可用 cp work/Store-before-save.java.txt Store.java 仅回退源代码并保留加载器。之后重新添加写入语句。不要重置借用台文件或重新运行 setup。
再次编译已保存的源代码:
javac -encoding UTF-8 -d out *.java
java -Dfile.encoding=UTF-8 -cp out Desk desk.tsv
在借用台内输入以下内容:
borrow T21 M01
borrow T21 M02
return T24 M01
borrow T23 M01
borrow TYPO M01
return T24 M03
list
quit
T21 被 M01 借用。重复借用、错误归还人、借用维修工具及拼错 ID 都会按预期被拒绝。只有 M03 归还 T24 会成功。T22 保持可借,T23 仍在维修,T25 仍由 M02 借用并保留原备注。未知 ID 的拒绝不会让命令循环失效;修正 ID,不要重启或重置。
现在用同一个文件启动一个新的 Java 进程:
java -Dfile.encoding=UTF-8 -cp out Desk desk.tsv
list
quit
T21 必须仍由 M01 BORROWED,T24 必须仍为 AVAILABLE,借用人为 -。否则,检查是否编译了当前的 Store.java,以及启动时是否省略了 --memory。不要编辑最终记录来伪装一次已保存的借用。如果合法操作已经保存,不要仅因为现在被拒绝就重复执行;list 显示当前事实。
回到 shell,读取实际存储的结果:
cat desk.tsv
记录关系必须与新进程的列表一致。保留所有 ID、名称、成员、维修状态、未涉及的借用和备注。只有 T21 的状态/借用人及 T24 的状态/借用人发生变化。这个小型写入器在成功保存时替换文件;它不提供并发访问、崩溃恢复或原子事务。如果写入失败,停止并检查错误,再继续处理。
总结
你把 UTF-8 记录映射为 Java 对象,在合法状态变化后保存每条当前记录,并在新进程中重新打开借用台。已有借用、维修状态和备注保持完整,被拒绝的操作没有写入存储。



