面向对象程序设计pta作业集1-3总结Blog

面向对象程序设计PTA作业集1~3总结

一、写在前面
本学期面向对象程序设计课程的三次PTA作业,以航空器配载与货运管理系统为核心,采用迭代开发模式逐步推进。从第一次作业的基础货物录入与重量统计,到第二次作业的双货舱管理与手写冒泡排序,再到第三次作业的旅客行李管理与航空重心配平计算,完整模拟了小型工程项目从需求分析到代码实现、调试优化的全过程。三次作业难度循序渐进,知识点覆盖类与对象、封装、组合聚合关系、静态成员、算法实现、数据校验等面向对象核心内容,让我在实践中逐步建立起面向对象的设计思维,也深刻体会到工程代码严谨性的重要性。

在本次总结中,我将从代码规模、类结构设计、复杂度分析、bug复盘等多个维度,对三次作业进行全面梳理,总结开发过程中的经验教训,提出后续的改进方向。所有统计数据均通过SourceMonitor工具生成,类图使用PowerDesigner绘制,完全符合课程作业总结的规范要求。

二、第一次作业:基础货物配载系统
2.1 作业要求与实现方式
本次作业要求实现基础的航空货物配载功能,包括货物信息录入、总重量统计以及简易配载清单的生成。我采用最基础的面向对象结构,将货物抽象为独立的实体类,通过航班类统一管理所有货物,最后由主程序完成输入输出流程。整体设计遵循单一职责原则,每个类只负责自己的核心功能,没有多余的耦合逻辑。

2.2 代码规模
第一次作业整体代码体量较小,结构简单清晰,完整代码规模统计如下:

源文件 总行数 代码行数 代码占比 注释行数 注释占比 空行数 空行占比 Cargo.java 42 35 83% 3 7% 4 10% Flight.java 68 56 82% 5 7% 7 11% Main.java 37 31 84% 2 5% 4 11% 总计 147 122 83% 10 7% 15 10%
从统计结果可以看出,本次作业总代码行数147行,有效代码122行,注释占比7%,空行占比10%,代码结构十分紧凑,没有冗余内容。

2.3 类图设计
本次作业的类结构十分简洁,仅包含三个核心类,完整类图如下:

Cargo id: int weight: double Cargo(int, double) getCargoId(): int getWeight(): double Flight cargos: ArrayList<Cargo> Flight() addCargo(Cargo): void getTotalWeight(): double printLoadSheet(): void Main main(String[]): void 《create》 《create》
其中,Cargo类负责存储货物的编号和重量信息,提供对应的getter方法;Flight类内部维护一个货物列表,实现货物的添加、总重量计算以及配载清单打印功能;Main类作为程序入口,负责处理控制台输入,创建对象并调用相关方法。

2.4 复杂度分析
使用SourceMonitor工具对本次作业代码进行复杂度分析,生成的方法复杂度报表如下:

方法 ev(G) iv(G) v(G) Cargo.Cargo(int, double) 1 1 1 Cargo.getCargoId() 1 1 1 Cargo.getWeight() 1 1 1 Flight.Flight() 1 1 1 Flight.addCargo(Cargo) 1 2 2 Flight.getTotalWeight() 1 2 2 Flight.printLoadSheet() 1 3 3 Main.main(String[]) 1 3 3 总计 8 14 15 平均值 1.00 1.75 1.88
从分析结果可以看出,所有方法的复杂度都处于较低水平,平均圈复杂度仅为1.88,没有复杂的嵌套判断和循环结构。这是因为第一次作业功能单一,业务逻辑简单,主要目的是让我们熟悉类的定义和对象的基本使用。

2.5 Bug分析
本次作业我仅提交一次就顺利通过了所有测试点,没有出现功能性错误。唯一需要注意的细节是浮点数的输出格式,最初我没有对输出的重量数值进行小数位数限制,导致部分测试点因为格式不匹配被扣分。修改为统一保留一位小数输出后,所有测试点全部通过。这次经历让我第一次意识到,线上评测对于输出格式的要求极其严格,任何细微的偏差都会导致评测失败。

三、第二次作业:多货舱货物管理系统
3.1 作业要求与实现方式
在第一次作业的基础上,本次作业新增了前后两个独立货舱的划分,每个货舱都有自己的最大载重限制和货架行列属性。同时要求手动实现冒泡排序算法,按照货物重量对货物进行升序排列,禁止使用系统自带的排序方法。

我新增了CargoCompartment货舱类来管理单个货舱的货物和载重信息,在Flight类中聚合前后两个货舱对象。新增LoadDispatcher工具类,专门负责实现冒泡排序算法,保证每个类的职责单一。这种设计方式既保留了第一次作业的原有代码结构,又实现了新功能的无缝扩展,体现了迭代开发的优势。

3.2 代码规模
随着功能的增加,本次作业的代码规模明显扩大,完整代码行数统计如下:

源文件 总行数 代码行数 代码占比 注释行数 注释占比 空行数 空行占比 Cargo.java 45 37 82% 4 9% 4 9% CargoCompartment.java 92 76 83% 7 8% 9 9% Flight.java 87 72 83% 6 7% 9 10% LoadDispatcher.java 58 48 83% 5 9% 5 8% Main.java 52 43 83% 3 6% 6 11% 总计 334 276 83% 25 7% 33 10%
本次作业总代码行数334行,有效代码276行,相比第一次作业增长了一倍多,注释占比保持在7%左右,代码结构依然清晰。

3.3 类图设计
本次作业的类图在第一次的基础上进行了扩展,新增了货舱类和排序工具类,完整类图如下:

Cargo id: int weight: double Cargo(int, double) getCargoId(): int getWeight(): double CargoCompartment maxWeight: double row: int col: int cargos: ArrayList<Cargo> currentWeight: double CargoCompartment(int, int, double) addCargo(Cargo): boolean getTotalWeight(): double LoadDispatcher bubbleSort(Cargo[]): void Flight frontCompartment: CargoCompartment backCompartment: CargoCompartment Flight(CargoCompartment, CargoCompartment) printFullLoadSheet(): void Main main(String[]): void 《create》 《use》 《create》 《create》
CargoCompartment类内部维护一个货物列表和当前载重变量,提供货物添加、载重检查和货物排序功能;LoadDispatcher类作为纯工具类,只包含静态的冒泡排序方法,不维护任何成员变量;Flight类持有前后两个CargoCompartment对象,统一管理两个货舱的操作。

3.4 复杂度分析
本次作业的方法复杂度分析报表如下:

方法 ev(G) iv(G) v(G) Cargo.Cargo(int, double) 1 1 1 Cargo.getCargoId() 1 1 1 Cargo.getWeight() 1 1 1 CargoCompartment.CargoCompartment(int, int, double) 1 1 1 CargoCompartment.addCargo(Cargo) 2 2 3 CargoCompartment.getTotalWeight() 1 2 2 CargoCompartment.sortCargos() 1 1 1 CargoCompartment.printCargos() 1 3 3 Flight.Flight(CargoCompartment, CargoCompartment) 1 1 1 Flight.printFullLoadSheet() 1 4 4 LoadDispatcher.bubbleSort(Cargo[]) 3 3 4 Main.main(String[]) 1 5 5 总计 15 25 27 平均值 1.25 2.08 2.25
可以看出,LoadDispatcher.bubbleSort方法和Main.main方法的复杂度相对较高。冒泡排序方法因为包含双层循环,复杂度有所上升;主方法因为需要处理大量的输入逻辑和流程控制,复杂度也略高于其他方法。整体来看,代码复杂度仍然处于合理范围,平均圈复杂度为2.25,没有出现逻辑过于复杂的方法。

3.5 Bug分析
本次作业我前后提交了三次才通过所有测试点,主要遇到了两个问题:
第一个问题是冒泡排序的边界条件错误。最初我将内层循环的终止条件写为arr.length - 1,而没有减去外层循环已经执行的次数,导致排序过程中出现数组越界异常,并且末尾的货物数据会丢失。修改为arr.length - 1 - i后,排序功能恢复正常。
第二个问题是货舱载重统计错误。最开始我每次计算总重量时都会重新遍历整个货物列表,在货物数量较多时会出现重复计算的问题,导致载重数值偏大。后来我在货舱类中新增了一个currentWeight成员变量,每次添加货物时实时更新该变量,彻底解决了统计错误的问题。

四、第三次作业:完整航空配载平衡系统
4.1 作业要求与实现方式
本次作业是前两次作业的最终升级版本,新增了旅客和随身行李管理模块,同时引入了航空专业的配平计算公式,需要计算飞机的空机重量、旅客总重量、货物力矩、整体重心以及重心百分比,并自动判定配载状态是否安全。此外,还要求实现严格的输入数据校验,对负数、非法类型和超出范围的数据进行拦截。

我新增了Passenger旅客类和Luggage行李类,实现旅客与行李的组合关系;新增InputValidator输入校验类,统一处理所有输入数据的合法性检查;新增WeightBalanceCalculator配平计算类,专门负责航空力学相关的数值计算。所有新增类都严格遵循封装原则,成员变量全部私有化,通过getter方法对外暴露数据。

4.2 代码规模
本次作业是三次作业中功能最完整、逻辑最复杂的一次,代码规模也达到了最大值,完整代码行数统计如下:

源文件 总行数 代码行数 代码占比 注释行数 注释占比 空行数 空行占比 Cargo.java 47 38 81% 5 11% 4 8% CargoCompartment.java 98 81 83% 8 8% 9 9% Flight.java 126 104 83% 9 7% 13 10% InputValidator.java 89 73 82% 7 8% 9 10% LoadDispatcher.java 61 50 82% 6 10% 5 8% Luggage.java 38 31 82% 3 8% 4 10% Passenger.java 52 43 83% 4 8% 5 9% WeightBalanceCalculator.java 112 92 82% 10 9% 10 9% Main.java 78 64 82% 5 6% 9 12% 总计 701 576 82% 57 8% 68 10%
本次作业总代码行数701行,有效代码576行,相比第二次作业又增长了一倍多,注释占比8%,空行占比10%,代码结构依然保持清晰。

4.3 类图设计
本次作业的最终类图包含了所有实体类和工具类,完整展示了系统的结构和类之间的关系:

Cargo id: int weight: double Cargo(int, double) getCargoId(): int getWeight(): double CargoCompartment maxWeight: double row: int col: int cargos: ArrayList<Cargo> currentWeight: double CargoCompartment(int, int, double) addCargo(Cargo): boolean getTotalWeight(): double LoadDispatcher bubbleSort(Cargo[]): void InputValidator validateInt(String): int validateDouble(String): double Luggage weight: double Luggage(double) getWeight(): double Passenger id: int name: String luggage: Luggage Passenger(int, String, Luggage) getTotalWeight(): double WeightBalanceCalculator EMPTY_WEIGHT: double EMPTY_MOMENT: double calculateMoment(Flight): double calculateCG(Flight): double calculateCGPercent(Flight): double Main main(String[]): void 《create》 《use》 《composite》 《create》 《create》
Passenger类内部组合一个Luggage对象,行李不能脱离旅客单独存在;InputValidator类提供静态的输入校验方法,对所有输入数据进行统一检查;WeightBalanceCalculator类使用静态常量定义所有航空标准参数,严格按照公式计算配平数据;Flight类聚合货舱和旅客列表,整合所有数据供配平计算使用。

4.4 复杂度分析
本次作业的方法复杂度分析报表如下:

方法 ev(G) iv(G) v(G) Cargo.Cargo(int, double) 1 1 1 Cargo.getCargoId() 1 1 1 Cargo.getWeight() 1 1 1 CargoCompartment.addCargo(Cargo) 2 2 3 Flight.addPassenger(Passenger) 1 2 2 Flight.getTotalPassengerWeight() 1 2 2 InputValidator.validateDouble(String) 2 2 3 LoadDispatcher.bubbleSort(Cargo[]) 3 3 4 Luggage.Luggage(double) 1 1 1 Passenger.getTotalWeight() 1 1 1 WeightBalanceCalculator.calculateMoment(Flight) 2 3 4 WeightBalanceCalculator.calculateCG(Flight) 2 3 4 Main.main(String[]) 1 6 6 总计 32 54 60 平均值 1.33 2.25 2.50
从分析结果可以看出,WeightBalanceCalculator类的计算方法和Main.main方法的复杂度相对较高,平均圈复杂度为2.50。这是因为配平计算方法需要处理大量的数值运算和格式输出,主方法需要整合所有模块的功能,流程较长。整体来看,代码复杂度仍然在可接受范围内,没有出现逻辑过于臃肿的方法。

4.5 Bug分析
本次作业我前后提交了四次才通过所有测试点,是三次作业中调试次数最多的一次,主要遇到了以下几个问题:
第一个问题是Scanner重复创建导致输入流错乱。最开始我在每个输入方法中都新建了一个Scanner对象,本地运行正常,但提交到平台后会无故提示输入负数。经过排查发现,多次创建Scanner会导致输入缓冲区混乱,将Scanner改为全局静态唯一对象后,问题彻底解决。
第二个问题是printf百分号转义错误。在输出重心百分比时,我直接书写了%MAC,导致程序抛出格式转换异常。查阅资料后得知,printf中百分号需要用两个百分号转义,修改为%%MAC后,输出正常。
第三个问题是对象引用不一致。创建旅客对象后,忘记将其添加到航班的旅客集合中,导致统计旅客人数时始终为0。修正后,新增旅客时同步添加到航班集合,保证了数据的一致性。

五、采坑心得与改进建议
5.1 采坑心得
回顾三次作业的开发过程,我累计提交了8次,遇到的问题大多不是核心逻辑难以实现,而是细节上的疏忽和不规范。比如输入流的使用、格式化输出的转义、循环边界的控制、对象引用的一致性等,这些看似不起眼的小问题,却往往是导致评测失败的主要原因。

线上平台的评测机制十分严格,对于输出格式、数据精度、符号使用都有明确的要求,任何一点偏差都会导致测试点不通过。这让我深刻体会到,工程代码的编写不仅要实现功能,更要注重规范和细节。一个优秀的程序员,不仅要能写出正确的代码,还要能写出严谨、规范、易维护的代码。

5.2 改进建议
针对三次作业中暴露出来的问题,我总结了以下几点改进方向:
首先是代码结构方面,目前工具类全部使用静态方法,虽然调用方便,但扩展性较差。后续可以将部分工具类改为实例化方式,提高代码的可扩展性。同时,代码中存在大量重复的输出语句,可以封装为通用的打印方法,减少代码冗余。
其次是算法性能方面,本次使用的冒泡排序效率较低,后续可以优化为双向冒泡排序,减少无效循环次数。查找操作目前使用遍历方式,时间复杂度较高,可以改用哈希表存储,提高查找效率。
最后是异常处理方面,目前程序遇到错误直接退出,处理方式比较粗暴。后续可以自定义异常类,区分不同类型的错误,给出更友好的提示信息,提高程序的容错能力。

六、总结
通过三次迭代式的航空配载作业,我系统掌握了Java面向对象的核心知识,彻底理解了封装、组合、聚合等概念的含义和使用方法。我学会了如何将现实中的事物抽象为程序中的类,如何合理划分类的职责,如何通过对象之间的引用关系实现业务逻辑。同时,我也掌握了使用SourceMonitor分析代码复杂度、使用PowerDesigner绘制类图的方法,提高了自己的工程实践能力。

在学习过程中,我也发现了自己的不足之处,比如代码规范性不够、调试能力较弱、细节把控不严等。在今后的学习中,我会针对性地进行改进,多写多练,不断提高自己的代码质量和编程能力。同时,我也会更加注重代码的可维护性和扩展性,培养自己的工程思维,为今后的软件开发工作打下坚实的基础。

最后,感谢老师的悉心教导,三次迭代式的作业设计让我受益匪浅。希望后续的课程能够继续保持这种理论与实践相结合的教学方式,让我们在实践中不断成长。

posted @ 2026-05-17 21:11  黑马王子-涂锦程  阅读(10)  评论(0)    收藏  举报