• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
鄢睿涛
博客园    首页    新随笔    联系   管理    订阅  订阅

面向对象设计与构造总结

Java编程作业总结报告(第1-3次)

一、前言

本阶段共完成三次Java编程作业,题目难度呈阶梯式上升,覆盖了从基础语法到面向对象设计的核心知识点。现对三次作业的整体情况进行总结:

知识点分布:

  • 第一次作业:Java基本语法、变量与数据类型、运算符与表达式、简单输入输出

  • 第二次作业:控制流程(分支与循环)、方法定义与调用、数组基本操作

  • 第三次作业:类与对象设计、封装性、构造方法、字符串处理、基本数据结构应用

题量与难度分析:

  • 题量设置合理,每次作业包含2-3个编程题目

  • 第一次作业以语法练习为主,难度较低,主要熟悉Java编程环境

  • 第二次作业增加逻辑复杂度,需要综合运用分支和循环结构

  • 第三次作业引入面向对象概念,要求设计类结构,难度明显提升

整体评价:作业体系设计循序渐进,既保证了基础知识的巩固,又逐步培养了面向对象的思维方式。通过三次作业,我从最初的代码结构混乱,逐渐形成了模块化、结构化的编程意识。

二、设计与分析

(一)第一次作业分析

1. 代码规模与结构

根据SourceMonitor生成的报表数据显示:

b6407c354aafb68f4fb61334e678062c

 

类图结构分析(参考PowerDesigner绘制):

a5e3517005ae80386dc7dce88b3c52a2

从类图可以看出,第一次作业采用了基础的三类结构:

  • Main类:包含程序入口main方法,负责整体流程控制

  • InputHandler类:专门处理输入数据的读取和初步验证

  • Calculator类:承载核心计算逻辑

这个设计初步体现了模块化思想,但存在以下特点:

  1. 各类间耦合度较高,Main类直接依赖具体实现类

  2. 方法粒度过细,Average Statements per Method仅为5.17,说明部分方法过于简短

  3. 缺少接口定义,扩展性不足

2. 复杂度分析

Screenshot 2026-05-18 193818

 

  • Avg Complexity:平均圈复杂度处于合理范围(约2.5)

  • Max Complexity:第5行方法复杂度最高,经分析为main方法中包含了输入判断和计算调用的混合逻辑

  • Avg Depth:平均嵌套深度约为2.1,存在多层if-else嵌套

  • Max Depth:最大深度达到4层,出现在输入验证部分

心得反思:
第一次作业让我认识到,即便是简单程序,不良的代码结构也会导致可读性下降。第5行方法复杂度过高的原因是未合理拆分职责,main方法既做输入判断又做计算控制,违背了单一职责原则。后续应将输入解析、验证、计算分别封装。

(二)第二次作业分析

1. 代码规模与结构:

24af039482dfa3920a76e6ab1312799d

第二次作业在第一次基础上进行了重构

类图结构优化:

5682b4f087d318bd0e1850f3d94a940c

 

第二次作业的类图展现了更清晰的职责划分:

  • Main类:简化为仅负责程序启动

  • InputValidator类:独立承担输入合法性验证

  • ProcessController类:新增的控制类,协调数据流向

  • BusinessLogic类:纯业务逻辑实现

关键改进点:

  1. 引入控制类降低Main类的复杂度

  2. 验证逻辑从业务逻辑中分离

  3. 方法粒度适中,平均语句数6.44比第一次更合理

2. 复杂度指标解读

Screenshot 2026-05-18 194800

 

报表中的"Kwiat Graph"展示了语句深度分布:

  • 深度1的语句占比最高(约40%)

  • 深度2-3的语句占35%

  • 深度4以上语句占25%

Block Histogram分析:
从statements vs. depth的柱状图可看出:

  • 多数语句深度集中在2-3层,代码结构相对扁平

  • 深度5以上的语句出现在循环嵌套的分支处理中

  • 最大深度区域的方法需要重点重构

心得反思:
第二次作业我重点关注了代码结构优化,但发现"Percent Branch Statements"提升到22%后,测试覆盖难度增加。有4个分支路径在初次测试时遗漏,导致运行时出现异常。这让我认识到:分支语句的增加必须以完善的单元测试为保障。

(三)第三次作业分析

1. 代码规模与结构

第三次作业是综合应用,面向对象特性更明显:

d8b80aef344c74cc13eaa056a67fc4bd

类图结构的进化:

3083e05423f854be29d069c10573200c

 

第三次作业的类图体现了面向对象的核心原则:

  • 抽象基类/接口:定义了统一的行为契约

  • 具体实现类:分别处理不同类型的数据和逻辑

  • 工厂模式:通过工厂类管理对象创建

  • 策略模式:算法可动态替换

关键设计要素:

  1. 使用继承减少重复代码

  2. 利用多态替代复杂的条件分支

  3. 封装数据和行为到合适的类中

2. 复杂度与质量分析

Screenshot 2026-05-18 201225

报表中的"Comments"项为0.0%值得注意:

  • 完全无注释的代码在团队开发中不可接受

  • 复杂算法和业务逻辑需要必要的说明文档

  • 类和方法的功能边界应通过注释明确

"Line Number of Most Complex Method"在第5行:
说明最复杂的方法位于文件开头,通常是初始化或主控方法。建议:

  • 将初始化过程拆分为多个私有方法

  • 使用配置对象集中管理参数

  • 考虑使用构建器模式处理复杂构造

心得反思:
第三次作业我重点实践了设计模式,但发现过度设计的问题。原本40行的代码用了设计模式后膨胀到120行,虽然扩展性提升了,但对于作业规模而言得不偿失。这让我认识到:设计模式的应用需要权衡,不能为了模式而模式。

三、采坑心得

(一)典型错误案例

错误1:数组越界异常(第二次作业)

代码场景:

java
// 错误代码示例
int[] scores = new int[5];
for(int i = 0; i <= scores.length; i++) {
    scores[i] = i * 10;
}

错误原因:循环条件使用了<=,当i等于5时访问scores[5]导致越界。

测试发现:通过边界值测试(输入5个数据)时程序崩溃。

解决方案:将循环条件改为i < scores.length。

流程图说明:

text
开始 → 初始化i=0 → 判断i<=数组长度? 
                              ↓ 是
                         访问scores[i]
                              ↓
                          i自增1
                              ↓ 否
                           程序崩溃

修正后:

text
开始 → 初始化i=0 → 判断i<数组长度?
                              ↓ 是
                         访问scores[i]
                              ↓
                          i自增1
                              ↓ 否
                           正常结束

教训总结:数组遍历时牢记索引范围是[0, length-1],使用<而非<=。


错误2:字符串比较错误(第一次作业)

代码场景:

java
// 错误代码
String input = scanner.next();
if(input == "quit") {
    System.exit(0);
}

错误原因:使用==比较字符串内容,实际比较的是对象引用地址。

测试数据:输入"quit"时程序无响应,未正常退出。

解决方案:改为if("quit".equals(input))。

更深层思考:equals()方法比较内容,==比较引用。字符串常量池的存在使得==有时碰巧正确,但这是不可靠的。

教训总结:字符串内容比较必须使用equals(),与null比较时建议将常量放在前面避免空指针。


错误3:整数除法精度丢失(第三次作业)

代码场景:

java
// 需求:计算平均分,保留两位小数
int total = 275;
int count = 4;
double average = total / count;  // 结果是68.0,而不是68.75

错误原因:两个int相除结果仍是int,小数部分直接被截断。

测试发现:输出平均分与预期不符,差值较大。

解决方案:

java
double average = (double) total / count;  // 强制类型转换
// 或
double average = total * 1.0 / count;

数据对比表:

 
totalcount错误结果正确结果绝对误差
275 4 68.0 68.75 0.75
100 3 33.0 33.33 0.33
10 3 3.0 3.33 0.33

教训总结:涉及除法运算时,确保至少一个操作数为浮点类型。


错误4:方法返回值未处理(第二次作业)

代码场景:

java
public int calculate(int a, int b) {
    // 某些条件下没有return语句
    if(a > b) {
        return a - b;
    }
    // 缺少else情况的return
}

错误原因:编译器报错"missing return statement",但在逻辑上认为不会执行到方法结尾。

测试发现:当a <= b时,方法没有返回值,编译不通过。

解决方案:

java
public int calculate(int a, int b) {
    if(a > b) {
        return a - b;
    }
    return b - a;  // 确保所有路径都有返回值
}

教训总结:非void方法必须确保所有执行路径都有return语句,包括异常处理分支。


(二)测试策略与数据

测试用例设计表(以第三次作业的某功能为例):

 
测试类型输入数据预期输出实际输出是否通过
正常值 5, 10 15 15 ✓
边界值-最小 0, 0 0 0 ✓
边界值-最大 9999, 1 10000 10000 ✓
负数 -5, 8 3 3 ✓
大数值 1000000, 2000000 3000000 3000000 ✓
空输入 (无输入) 提示错误 提示错误 ✓
非数字 abc, 123 输入格式错误 程序崩溃 ✗

缺陷分析:
第7个测试用例失败暴露了输入验证的不足。改进方案:

  1. 使用try-catch捕获InputMismatchException

  2. 调用hasNextInt()预先判断

  3. 使用正则表达式验证输入格式

测试覆盖率追踪:

  • 第一次作业:行覆盖率约70%,分支覆盖率约50%

  • 第二次作业:行覆盖率约85%,分支覆盖率约70%

  • 第三次作业:行覆盖率约90%,分支覆盖率约80%

四、改进建议

(一)代码结构层面

1. 降低耦合度

  • 问题:Main类直接实例化和调用具体类,修改实现时需要改动Main类

  • 改进:引入依赖注入或工厂模式

java
// 改进前
Calculator calc = new Calculator();

// 改进后
ICalculator calc = CalculatorFactory.create();

2. 提升内聚性

  • 问题:部分类承担了过多职责,如既处理输入又处理验证

  • 改进:按照单一职责原则拆分

    • 输入读取类:仅负责从数据源获取原始数据

    • 输入验证类:仅负责校验数据合法性

    • 数据转换类:仅负责格式转换

3. 注释规范

  • 问题:三次作业注释率均为0%,严重影响可维护性

  • 改进标准:

    • 每个类前:说明类的职责和主要功能

    • 每个公共方法前:说明参数、返回值、异常、算法思路

    • 复杂代码块前:解释为什么要这样写,而非描述代码本身

java
/**
 * 计算两个整数的最大公约数
 * @param a 第一个整数,可为正数或负数
 * @param b 第二个整数,可为正数或负数
 * @return 最大公约数,始终为非负数
 * @throws IllegalArgumentException 如果a和b同时为0
 */
public int gcd(int a, int b) {
    // 使用欧几里得算法(辗转相除法)
    // 因为对负数取模的结果在不同语言中不一致,先取绝对值
    a = Math.abs(a);
    b = Math.abs(b);
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

(二)算法优化

1. 输入验证优化

  • 现状:使用多层if-else嵌套,最大深度4层

  • 改进:使用表驱动法或责任链模式

java
// 改进前
if(input != null) {
    if(!input.isEmpty()) {
        if(input.matches("\\d+")) {
            if(Integer.parseInt(input) >= 0) {
                // 处理
            }
        }
    }
}

// 改进后
ValidationRule[] rules = {
    new NotNullRule(),
    new NotEmptyRule(),
    new NumericRule(),
    new NonNegativeRule()
};
if(Validator.validate(input, rules)) {
    // 处理
}

2. 循环优化

  • 问题:某些循环内执行了重复计算

  • 改进:将循环不变代码外提

java
// 改进前
for(int i = 0; i < list.size(); i++) {
    int length = list.size();  // 重复调用
    // ...
}

// 改进后
int length = list.size();
for(int i = 0; i < length; i++) {
    // ...
}

(三)异常处理机制

现状问题:

  • 未区分可恢复异常和不可恢复异常

  • 异常捕获后仅打印堆栈,未给用户友好提示

  • 存在空的catch块

改进方案:

  1. 建立异常处理分层

    • 底层:抛出具体异常(如IllegalArgumentException)

    • 中层:捕获并转换业务异常

    • 上层:统一异常处理并返回用户友好信息

  2. 自定义业务异常类

java
public class BusinessException extends Exception {
    private String errorCode;
    private String userMessage;
    
    public BusinessException(String errorCode, String userMessage) {
        this.errorCode = errorCode;
        this.userMessage = userMessage;
    }
    
    public String getUserMessage() {
        return userMessage;
    }
}
  1. 资源管理使用try-with-resources

java
// 自动关闭资源
try (Scanner scanner = new Scanner(System.in);
     FileWriter writer = new FileWriter("output.txt")) {
    // 使用资源
}

(四)可测试性提升

问题:代码难以进行单元测试,因为逻辑分散在静态方法中

改进:

  1. 避免使用静态方法进行业务逻辑处理

  2. 使用接口定义服务边界

  3. 提供Mock实现便于测试

java
public interface IDataService {
    Result process(InputData data);
}

// 生产实现
public class DataServiceImpl implements IDataService { }

// 测试Mock
public class MockDataService implements IDataService {
    public Result process(InputData data) {
        // 返回预定义的结果
    }
}

五、总结

1. 技术能力提升

  • 掌握了Java基本语法和常用类库(Scanner、String、Arrays等)

  • 理解了面向对象的三大特性:封装、继承、多态

  • 学会了使用SourceMonitor进行代码质量分析,用PowerDesigner绘制类图

  • 建立了基本的测试意识,能够设计边界值、等价类等测试用例

2. 工程能力提升

  • 从面向过程思维逐步转向面向对象设计

  • 认识到代码的可读性和可维护性与正确性同等重要

  • 学会了通过重构改善代码结构

  • 理解了设计模式的实际应用场景和权衡

3. 问题解决能力提升

  • 通过分析SourceMonitor指标定位代码复杂度过高的方法

  • 使用流程图梳理复杂逻辑,降低认知负担

  • 通过表格化测试数据,系统性地验证程序正确性

结语:这三次作业不仅是编程技能的锻炼,更是思维方式的转变。从“让程序跑起来就行”到“如何让代码更清晰、更健壮、更易维护”,这个观念的转变至关重要。虽然过程遇到了各种问题,但每一次调试、每一次重构都带来了实实在在的成长。感谢老师的精心设计和指导,我会继续努力,在后续的学习中持续精进。

 
 
posted @ 2026-05-18 21:01  鄢睿涛  阅读(6)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3