第一周编程作业

第一次个人编程作业

GitHub 链接https://github.com/3124004103ls/3124004103ls


一、需求

题目:论文查重

描述如下:

设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。

  • 原文示例:今天是星期天,天气晴,今天晚上我要去看电影。
  • 抄袭版示例:今天是周天,天气晴朗,我晚上要去看电影。

要求输入输出采用文件输入输出,规范如下:

  • 从命令行参数给出:论文原文的文件的绝对路径。
  • 从命令行参数给出:抄袭版论文的文件的绝对路径。
  • 从命令行参数给出:输出的答案文件的绝对路径。
    我们提供一份样例,课堂上下发,上传到班级群,使用方法是:orig.txt是原文,其他orig_add.txt等均为抄袭版论文。

注意:答案文件中输出的答案为浮点型,精确到小数点后两位

PSP 表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 40
· Estimate · 估计这个任务需要多少时间 30 40
Development 开发 380 600
· Analysis · 需求分析(包括学习新技术) 60 90
· Design Spec · 生成设计文档 30 45
· Design Review · 设计复审 20 30
· Coding Standard · 代码规范 20 30
· Design · 具体设计 40 60
· Coding · 具体编码 120 180
· Code Review · 代码复审 30 45
· Test · 测试 60 120
Reporting 报告 110 120
· Test Repor · 测试报告 30 45
· Size Measurement · 计算工作量 20 30
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 30 45
合计 490 760

二、项目结构

3124004103/
├── src/
│   ├── main/
│   │   └── java/
│   │       └── com/
│   │           └── example/
│   │               ├── Main.java                    # 程序入口
│   │               ├── TextProcessor.java           # 文本预处理与分词
│   │               └── SimilarityCalculator.java    # 余弦相似度计算
│   └── test/
│       └── java/
│           └── com/
│               └── example/
│                   ├── TextProcessorTest.java           # 文本处理单元测试
│                   ├── SimilarityCalculatorTest.java    # 相似度计算单元测试
│                   └── PerformanceTest.java             # 性能测试
├── target/                       # 编译输出(.gitignore 已忽略)
│   ├── classes/
│   ├── test-classes/
│   ├── main.jar                  # 打包后的可执行 jar
│   └── site/
│       └── jacoco/
│           └── index.html        # JaCoCo 覆盖率报告
├── pom.xml                       # Maven 配置
├── README.md                     # 项目说明
└── .gitignore                    # Git 忽略规则

三、计算模块接口的设计与实现过程

1. 代码组织

项目包含三个核心类:

职责
Main 程序入口,解析命令行参数,读写文件
TextProcessor 文本预处理与分词(中文按字 + bigram)
SimilarityCalculator 余弦相似度计算

类之间的关系

Main → SimilarityCalculator → TextProcessor
  • Main 负责 I/O
  • SimilarityCalculator 负责核心算法
  • TextProcessor 负责文本处理,被 SimilarityCalculator 调用

2. 算法关键

余弦相似度

cos = (A · B) / (|A| × |B|)
  • A、B 是两段文本的词频向量
  • 点积越大、模长越小,相似度越高

3. 独到之处

  • bigram 增强:不仅统计单字,还统计相邻二字组合,对“增删改”场景更敏感
  • 异常处理完善:文件不存在、不可读、空文件都有对应处理
  • 性能优化:tokenize 用 substring 替代 String.valueOf,减少临时对象

四、计算模块接口部分的性能改进

改进前(VisualVM 采样)

ccff00f40b4db509e944effe57225953

tokenize - Self time: 18.6 ms (71.3%)
         - String.valueOf: 7.52 ms (28.7%)

瓶颈:String.valueOf(char) 创建大量临时 String 对象。

改进后(PerformanceTest)

方法 100 次耗时 单次耗时
tokenize 548 ms 约 5.48 ms
calculate 2150 ms 约 21.5 ms

优化点

  • 去掉 chars 中间列表
  • 用 s.substring(i, i + 1) 和 s.substring(i, i + 2) 替代 String.valueOf

性能分析图
0c0bc254799308d9512863b785dba223


五、计算模块部分单元测试展示

测试用例

测试类 测试数 内容
TextProcessorTest 6 中文分词、空输入、null、标点、英文小写、中英混合
SimilarityCalculatorTest 10 相同文本、完全不同、高度相似、空文本、null、边界、忽略标点、忽略大小写、长文本
PerformanceTest 1 性能测试
合计 17

测试覆盖率

  • 指令覆盖率:61%
  • 分支覆盖率:80%
  • 行覆盖率:61%

ec581e6dae7ee07ee1ef35a4d633d581
2659c4236154d6b4c851e6543ef631bd


六、计算模块部分异常处理说明

异常场景 处理方式 单元测试
文件不存在 抛 IOException,输出错误信息
文件不可读 抛 IOException
空文本 返回空列表 / 相似度 0.0 testTokenizeEmptyCases、testBothEmpty
null 输入 返回空列表 / 相似度 0.0 或 1.0 testTokenizeEmptyCases、testNullInput
只有标点 返回空列表 testTokenizeEmptyCases

七、Code Quality Analysis

使用 SonarQube for IDE 对项目做代码质量分析,消除所有警告。

主要修复

  • S106:System.out/err 改用 Logger
  • S1118:工具类加私有构造函数
  • S2629:LOGGER.info(() -> ...) 避免字符串拼接
  • S5786:测试类去掉多余的 public
  • S5976:合并类似测试为参数化测试

最终状态:No SonarQube issues to display.

3a6b108dbe99a432bf85a43e1aeefa15

八、总结与反思

收获

  • 工程方面:第一次完整走完“需求分析 → 设计 → 编码 → 测试 → 性能优化 → 代码质量分析”的流程。
  • 工具方面:熟悉了 Git、Maven、JUnit 5、JaCoCo、SonarQube、VisualVM 等工具的使用。
  • AI 辅助开发:本次项目全程在 AI 辅助下完成,AI 帮助我梳理了算法思路、生成了代码框架、排查了编码/性能/覆盖率等问题,让我能把更多精力放在理解原理和解决实际问题上。

遇到的问题

  1. 中文编码问题:Java 文件默认用 GBK 保存,Maven 编译时按 UTF-8 读取,导致中文乱码、测试失败。解决方法是在 pom.xml 里加 maven-compiler-plugin 并指定 UTF-8。
    5f4689ab64c216b39e9d247548bf3517

  2. 性能瓶颈定位:VisualVM 在 Dragonwell JDK 下 JMX 连接不稳定,程序都跑完了还在Opening,最终改用 PerformanceTest 代码计时,成功定位 tokenize 中的 String.valueOf 是主要开销。
    e90ae76bda3f9cef24159fbde36c9593

  3. 测试覆盖率:一开始只写了功能测试,加上 JaCoCo 后才看到具体覆盖率,发现 Main 类覆盖率为 0(因为它是入口类,不被单元测试调用)。
    af9deb45ac640b91ae9fa8d882b14568

关于 AI 辅助的思考

  • AI 适合做“重复性工作”:比如生成测试用例模板、写 pom.xml 配置、整理博客结构,这些交给 AI 很高效。
  • AI 的局限:环境相关的坑:由于之前的Java课下载的是 Dragonwell JDK,这次我就直接用了。而它的 JMX 问题导致使用visualvm时程序都跑完了还在连接,这些问题AI 不一定能一次解决,需要自己动手试错。
posted @ 2026-09-13 20:45  luosheng24  阅读(10)  评论(0)    收藏  举报