Java面向对象程序设计——作业集1~3总结Blog
一、前言
面向对象程序设计课程的前三次PTA作业围绕“航空器配载与货运管理系统”这一真实业务场景展开,从这次任务中我充分理解到了:- 类与对象的基本设计
- 类间关系(组合,聚合,继承)
- 类与类之间的整合
二、设计与分析
2.1 第一次作业:基础货运配载模块
作业要求:
【本次作业需求说明】
设计一个基础的航班货运配载模块。系统需要记录航班的基本信息(航班号、最大起飞重量、最大业载重量)。地勤人员可以按照货物重量从高到低向该航班添加货物(货物名称、重量)。系统需要实时计算当前已装载的总重量,并判断是否超载。
【设计要求】
类设计必须符合单一职责原则(SRP),参考类图如下所示(仅作参考,可自行加工具类)。
注意:类设计如果违背了SRP,此题不得分。
核心类:
- Main
- Flight
- Cargo
- LoadManifest
类间关系:

复杂度分析:

心得:第一次作业让我认识到,类设计的首要任务是明确每个类的职责边界。提前规划好“这个类负责什么”是写出可维护代码的基础。哪怕代码量少,如果职责划分混乱,后期修改成本也会成倍增长,本次实践让我初步建立面向对象设计意识:类设计的核心是“职责划分”,每个类只负责一项明确功能,避免“万能类”。
2.2 第二次作业:多货舱管理与重量排序装载
作业要求:
【本次作业需求说明】
设计一个扩展的航班货运配载模块,实现以下功能:
- 航班信息管理:记录航班号、最大起飞重量、最大业载重量。
- 货舱管理:
每个货舱有唯一标识(如“前舱”)、最大载重、行数、列数(组成位置网格)。
货舱与其位置是组合关系(CargoCompartment 创建时内部生成 Position 列表)。
货舱与装载的货物是聚合关系(Cargo 可独立存在)。
- 货物装载:
输入所有待装载货物(名称、重量、目标货舱ID)。
系统先按货物重量从高到低排序,再依次尝试将货物装入指定的货舱。
如果目标货舱的当前重量 + 该货物重量 ≤ 该货舱最大载重,则装载成功,否则装载失败并给出提示。
- 输出要求:
按排序后的顺序输出每件货物的装载结果(成功或失败)。
输出每个货舱的已装载总重量及状态(是否超载)。
输出航班整体总重量及与最大起飞重量、最大业载重量的对比,判断整体是否超载。
【设计要求】
必须严格遵循单一职责原则(SRP),不得将多个职责合并到一个类中。违背 SRP 本题不得分。
必须实现以下类(参考提供的类图与代码框架):
Position:表示货舱中的一个位置(行、列),提供 getPosName() 等方法。
Cargo:表示货物,包含 id(货物名称)、weight。
CargoCompartment:货舱类,包含 id、maxWeight、positions(组合)、cargos(聚合),提供 addCargo(Cargo cargo)、getCurrentWeight() 等方法。
Flight:航班类,包含航班号、最大起飞重量、最大业载重量、List。
LoadDispatcher:调度类,负责对货物列表按重量降序排序(sortCargos)、查找货物(FindCargo)。
InputValidator:输入校验类,提供整数、浮点数的范围校验方法。
类间关系:

复杂度分析:

心得:第二次作业让我深刻认识到,类间关系的选择(组合还是聚合、依赖还是关联)是业务语义的准确映射。Position作为货舱的组成部分——是组合;Cargo可以在货舱间转移,独立存在——是聚合。一旦关系选错,设计根基就会动摇。
2.3 第三次作业:航空器配平计算
作业要求:
【设计要求】
本次迭代新增约4个类,系统总类数达到10个左右。严禁使用继承和多态(无extends、无interface、无instanceof),请严格遵循以下关系与单一职责设定,注:原有的 Flight 类需增加List<Passenger> 属性,体现关联关系。原有的 LoadDispatcher(排序)在本次生成报告时需被内部调用。另外,本次作业中的排序算法不允许使用朗姆达表达式及Collections.sort()方法,必须使用循环完成排序过程(算法采用冒泡排序)
类间关系:

复杂度分析:

心得:在这一道题中测试点没有成功全部通过,在这一方面我深刻认识到面向对象的复杂性。并且,在Main类承担了过多流程调度与数据处理职责,违背单一职责原则,导致代码臃肿、可读性差,计算方面倘若数据过多很容易出错,需要在进行更改,考虑更加高效的排序策略。
三、问题
在三次作业的编码与调试过程中,我遭遇了多个典型问题,有些源于设计疏漏,有些暴露在测试边界。
3.1 排序算法的稳定性与实现细节
第一次作业使用Java内置排序工具,实现简单、效率高,无需关注底层逻辑。但第三次作业强制要求手写冒泡排序,禁止使用Lambda与内置排序方法,这一要求暴露了我对基础排序算法细节掌握不扎实的问题。
实现冒泡排序时,我最初错误地将内层循环终止条件写为j < list.size() - i,遗漏了-1,导致最后一轮循环访问了已排序完成的尾部元素。虽然测试数据量较小,未触发运行时错误,但逻辑上存在明显漏洞,属于典型的边界条件错误。
本次教训深刻:算法实现必须逐行验证逻辑,尤其是循环边界、数组下标、终止条件等细节;将排序逻辑封装在独立的LoadDispatcher类中是良好设计,当排序需求变更时,仅需修改该类,不影响其他业务代码,充分体现高内聚低耦合优势。
3.2 正则表达式与语义校验的边界
第二次作业处理输入数据时,我最初尝试用正则表达式统一验证所有数值输入,意图一次性拦截非数字字符串。但正则表达式仅能判断输入格式是否为数字,无法校验数值的业务合法性,例如负数、零值、超出范围的数值。
这一设计导致“-50”这类负数输入绕过格式校验,直接进入业务逻辑层,引发货物重量、货舱载重等关键数据异常,导致后续重量计算、超载判断全部出错。直到测试阶段才发现该漏洞,浪费大量调试时间。
后续解决方案采用“双重校验机制”:第一层用Scanner的hasNextInt()、hasNextDouble()方法判断输入格式合法性,拦截非数字输入;第二层由独立的InputValidator工具类,校验数值是否为非负、是否在指定业务范围内。分层校验彻底堵住漏洞,提升系统可靠性。
3.3边界条件测试不充分
第三次作业测试阶段,第十个测试点始终无法通过,反复排查后发现是边界条件处理不当:当货物重量等于货舱剩余载重、旅客重量为零、重心处于安全临界值时,代码未正确处理“等于”场景,导致判断逻辑错误。
反思发现,我设计测试用例时,仅关注常规场景,未系统性覆盖等价类边界值,尤其是最大值、最小值、零值、临界值等特殊情况。边界条件是程序最易出错的地方,也是测试的核心重点。
本次教训:测试用例设计需遵循“等价类划分+边界值分析”原则,全面覆盖正常、异常、边界场景;对于数值比较、状态判断等逻辑,必须严格区分“大于”“小于”“等于”三种情况,避免逻辑疏漏。
四、总结
回顾这三次作业的迭代历程,我从一个仅掌握Java基础语法的初学者,逐步成长为能够独立设计10余个类协同工作的面向对象编程学习者。具体收获如下:
- 深刻理解了面向对象的核心原则。封装、单一职责、类间关系不再是书本概念,而是指导编码的实战准则。每一次需求变更都成为检验设计柔性的试金石,让我切身感受到“高内聚、低耦合”的价值。
- 掌握了专业工具辅助开发的能力。PowerDesigner让类间关系一目了然,SourceMonitor将代码质量从“感觉”提升到“数据”。这些工具在后续学习设计模式和大规模系统开发中将持续发挥价值。
- 积累了丰富的调试与重构经验。从浮点精度陷阱到排序边界错误,从正则滥用到头文件膨胀,每个坑都深化了对语言特性和工程实践的理解。面对bug时,已能系统性地从数据、逻辑、测试多维度定位根因。
本次实践也暴露了我存在的不足:类间关系建模能力仍需加强,对组合、聚合、依赖等关系的语义理解不够精准;算法基础薄弱,手写代码时细节把控不足;测试用例设计能力欠缺,边界场景覆盖不全面。后续学习中,我将针对性强化这些短板,多做设计练习、夯实算法基础、规范测试流程,进一步提升面向对象设计与开发能力。
总而言之,本次航空器配载与货运管理系统的三次迭代实践,是一次宝贵的成长经历。它不仅提升了我的编程技能,更培养了工程思维、责任意识和严谨态度,为后续专业学习和职业发展奠定坚实基础。

浙公网安备 33010602011771号