穷且益坚,不坠青云之志-java课程总结
穷且益坚,不坠青云之志-java课程总结
岁聿云暮,星霜荏苒。忆昔初涉Java之域,面对封装继承尚觉晦涩难明,调试报错每每令人辗转反侧;而今已能运面向对象之初心,反复学习尝试,实现事件驱动,成pta之结果。这一程,从命令行到图形界面的探索,这一路从语法细节到设计模式的思考,既有深夜调试的困顿,亦有灵光乍现的欣喜。正如标题所示,我自认并不是天赋异禀,却也能摸爬滚打至今,这段话给你,也给我,愿我们上下求索之时,穷且益坚,不坠青云之志!
本课程的教学安排十分紧凑,包含了PTA编程作业实验课实践线上视频学习以及线下理论讲解等多个环节。从工作量来看,每周我都会抽出单独的时间进行代码的编写,其中PTA作业约3-4小时,实验的代码与报告的撰写2-3小时,线上课程也是其中一部分,比如黑马程序员的课程,以及最后线下课程中的翻转课堂。我想我度过了一个合理且充实的专业核心课程。
难度方面,课程呈现出明显的递进特征。前期的封装与继承概念相对容易掌握,中期多态与接口的理解需要反复琢磨,连续三次的电梯问题让我们"痛不欲生",货运系统设计让我们眼花缭乱,大象装进冰箱的实验陪伴我们走过一学期,总算是跟难兄难弟一起被塞进去,每一次代码都在强化对于知识点的理解与掌握,后期JavaFX的应用开发则对综合能力提出了更高要求。特别是从命令行程序转向图形界面开发时,思维方式的转变曾让我一度感到吃力。
那么按照要求,我们来进行不同版块的复盘:
一:封装 面向对象的基石
封装作为面向对象三大特性之首,是本课程最先接触也是贯穿始终的核心概念。通过PTA作业中"设计一个银行账户类"这样的题目,我第一次深刻理解了如何将数据(属性)和操作数据的方法捆绑在一起,并通过访问修饰符private控制外部访问的权限。
在实验一中,设计"学生信息管理系统"时,我最初将所有属性都设为public,导致后期维护困难。经过老师指导后,我学会了合理使用private修饰属性,并通过getter/setter方法提供可控的访问接口。这种"信息隐藏"的思想不仅提高了代码的安全性,更使得类的内部实现可以自由变化而不影响外部调用。
当然,封装的用处可不止于此,我想这个代码只是出顾茅庐而已,我们在电梯的代码中也经历过这种踩坑的折磨,就是在代码当中把各个功能写的鱼龙混杂:
点我!^_^
class Elevator {
private final int minFloor, maxFloor;
private int currentFloor;
private Direction direction;
// 队列定义
private final PriorityQueue<Integer> internalUp = new PriorityQueue<>(); // 内部上行目标
private final PriorityQueue<Integer> internalDown = new PriorityQueue<>(Comparator.reverseOrder()); // 内部下行目标
private final PriorityQueue<Request> externalUp = new PriorityQueue<>(); // 外部上行请求
private final PriorityQueue<Request> externalDown = new PriorityQueue<>(Comparator.reverseOrder()); // 外部下行请求
public Elevator(int min, int max) {
this.minFloor = min;
this.maxFloor = max;
this.currentFloor = min;
this.direction = Direction.STOPPED;
}
// 添加内部请求(按方向拆分)
public void addInternal(int floor) {
if (floor < minFloor || floor > maxFloor) return;
if (floor > currentFloor) internalUp.add(floor);
else internalDown.add(floor);
}
// 添加外部请求(按方向存储)
public void addExternal(int floor, Direction dir) {
if (floor < minFloor || floor > maxFloor) return;
if (dir == Direction.UP) externalUp.add(new Request(floor, dir));
else externalDown.add(new Request(floor, dir));
}
public void run() {
while (hasPendingRequests()) {
adjustDirection();
moveOneStep();
checkStop();
}
}
private boolean hasPendingRequests() {
return !internalUp.isEmpty() || !internalDown.isEmpty() ||
!externalUp.isEmpty() || !externalDown.isEmpty();
}
// 核心方向调整逻辑
private void adjustDirection() {
if (direction == Direction.STOPPED) {
// 初始方向选择:优先处理最近的请求
if (!externalUp.isEmpty()) {
direction = (externalUp.peek().floor >= currentFloor) ? Direction.UP : Direction.DOWN;
} else if (!externalDown.isEmpty()) {
direction = (externalDown.peek().floor <= currentFloor) ? Direction.DOWN : Direction.UP;
} else if (!internalUp.isEmpty()) {
direction = Direction.UP;
} else {
direction = Direction.DOWN;
}
} else {
// 运行中方向检测:是否有同方向请求
boolean hasSameDirRequests = (direction == Direction.UP) ?
(!internalUp.isEmpty() || !externalUp.isEmpty()) :
(!internalDown.isEmpty() || !externalDown.isEmpty());
if (!hasSameDirRequests) {
direction = (direction == Direction.UP) ? Direction.DOWN : Direction.UP;
}
}
}
// 单步移动逻辑
private void moveOneStep() {
if (direction == Direction.UP && currentFloor < maxFloor) {
currentFloor++;
} else if (direction == Direction.DOWN && currentFloor > minFloor) {
currentFloor--;
}
System.out.printf("Current Floor: %d Direction: %s\n", currentFloor, direction);
}
// 停靠检测与处理
private void checkStop() {
boolean shouldStop = false;
// 处理内部请求
if (!internalUp.isEmpty() && internalUp.peek() == currentFloor) {
internalUp.poll();
shouldStop = true;
} else if (!internalDown.isEmpty() && internalDown.peek() == currentFloor) {
internalDown.poll();
shouldStop = true;
}
// 处理外部请求
if (direction == Direction.UP && !externalUp.isEmpty() && externalUp.peek().floor == currentFloor) {
externalUp.poll();
shouldStop = true;
} else if (direction == Direction.DOWN && !externalDown.isEmpty() && externalDown.peek().floor == currentFloor) {
externalDown.poll();
shouldStop = true;
}
if (shouldStop) {
System.out.printf("Open Door # Floor %d\n", currentFloor);
System.out.println("Close Door");
}
}
}
但是封装可以解决这一点:
在Elevator类的设计中,minFloormaxFloorcurrentFloordirection等关键属性都被严格声明为private,犹如为电梯运行状态构筑了一道安全屏障。这种设计理念绝非简单的"上锁",而是蕴含着深刻的工程智慧——以currentFloor为例,若允许外部直接修改,则电梯可能陷入"欲上忽下"的逻辑混乱,甚至引发严重的安全隐患。通过封装,我们强制所有状态变更都必须经由构造方法或addRequest等受控接口进行,确保电梯始终遵循预设的运行逻辑。
方法封装同样精妙:Elevator类中,每个方法都如齿轮般各司其职。addInternal方法专注处理内部请求的入队校验,adjustDirection方法则精密计算运行方向,二者通过明确定义的接口协同工作。这种"分而治之"的设计哲学,使得代码结构如清明上河图般层次分明——当需要优化请求调度算法时,开发者可直指addInternal而无需担忧方向逻辑;若需调整运行策略,亦只需聚焦adjustDirection而不必顾虑请求队列。这种可维护性,正是封装赋予我们的编写代码时最大的帮助.
二:继承 代码复用的艺术
我们来重新回顾一下初见继承时的题目:
当时初学时不是很理解,以遥控器来帮助记忆我个人觉得还是不错的:
就像是这样:
万能遥控器(Element类),它可以控制各种电器。所有电器都必须有"开/关"功能(display()方法)。
现在有几种电器:
电灯(Point 类):按开关会亮灯 💡
空调(Line 类):按开关会吹冷风 ❄️
电视(Plane 类):按开关会播放节目 📺
越到后期,其实发现继承的作用非常明显,能够帮助我们减少很多代码的重复,比如在题集9的魔方问题中,这个类图就很清晰的告诉我们各个类之间的关系:
那么复盘到这里,我又去重新把这道题目敲了一遍,我想这道题真的很具有代表性,从类图开始,这道题便在埋下伏笔,将一个个大魔方,拆成一个个小魔方,然后将每一个小魔方的计算框在一个Solid类里面,这样用来规范每一个小方块的计算流程,最后只要输入对应的小方块数据,程序就会一层层的往上计算,"拼凑"出一个个完整的大魔方
继承的优点在这个程序中体现
代码复用:
所有子类共享Solid中定义的side属性和getter/setter方法
不需要在每个子类中重复定义这些基本内容
多态性:
display()方法接收RubilkCube参数,但可以处理任何子类对象
运行时根据实际对象类型调用相应的方法实现
扩展性:
可以轻松添加新的立体形状或魔方类型,只需继承相应基类
不需要修改现有代码,符合开闭原则
抽象与具体分离:
高层代码(如Main)只需要知道RubilkCube的接口
具体实现细节隐藏在子类中
那么第三个,与继承打配合的:
多态: 灵活编程的关键
多态概念的学习曲线相对陡峭,但也是面向对象最强大的特性之一。
它就像「同样的按钮,按出不同效果」
多态和继承就像"遥控器"和"电器"
万能遥控器(Element类),它可以控制各种电器。所有电器都必须有"开/关"功能(display()方法)。
现在有几种电器:
电灯(Point 类):按开关会亮灯 💡
空调(Line 类):按开关会吹冷风 ❄️
电视(Plane 类):按开关会播放节目 📺
多态:父类引用统一调用,但实际执行看子类具体实现;
在实验题中中"动物叫声模拟"题目让我初识多态的魔力——同一方法调用在不同子类对象上产生不同行为.
同样,在刚刚的那道魔方题中,我们也同样运用了多态的相关知识:
这段代码中体现了面向对象编程中多态(Polymorphism)的多个关键应用,下面从我们从不同维度进行详细解析:
一、继承体系中的多态基础
核心体现:
1. RubilkCube、Solid等抽象类的设计
2. display()方法参数使用父类类型RubilkCube
static void display(RubilkCube cube) { // 父类引用作为参数
System.out.println(cube.getColor());
System.out.printf("%.2f\n", cube.getArea()); // 动态绑定到具体子类实现
System.out.printf("%.2f\n", cube.getVolume());
}
当传入SquareCube或RegularPyramidCube实例时,JVM会根据实际对象类型动态调用对应的getArea()和getVolume()方法,这就是运行时多态。
二、方法重写(Override)的多态
1. SquareCube和RegularPyramidCube对getArea()的不同实现:
// 正方体魔方表面积计算
(SquareCube) 6 * actualSide²
// 三棱锥魔方表面积计算
(RegularPyramidCube) √3 * actualSide²
2. 体积计算同样呈现差异化实现
代码特点:
- 调用方只需知道RubilkCube接口
- 新增魔方类型时无需修改display()方法
- 符合开闭原则(对扩展开放,对修改关闭)
三、组合关系中的多态
protected Solid solid; // 组合的几何体对象
// 构造方法注入具体实现
new SquareCube(color, layer, new Cube(side))
new RegularPyramidCube(color, layer, new RegularPyramid(side))
多态表现:
1. 通过Solid抽象类引用指向不同的具体子类(Cube/RegularPyramid)
2. 在计算体积/面积时,自动调用对应几何体的计算方法:
// 在SquareCube.getVolume()中
this.getSolid().getSide() // 动态绑定到Cube或RegularPyramid的实现
因此,我们可以大致总结一下这里关于多态的设计优势:
1. 扩展性:新增魔方类型只需继承RubilkCube
2. 维护性:修改具体实现不影响接口契约
3. 解耦合:display()方法仅依赖抽象不关注具体类型
4. 算法复用:共用layer放大逻辑,差异化实现核心公式
在航空货运管理系统中,我们带着这样的眼光去重新审视,便能得到:
1. 策略模式实现
calculateRate rateStrategy; // 接口引用
if ("Expedite".equals(cargoType)) {
rateStrategy = new ExpediteRate(); // 多态赋值
}
- 运行时根据货物类型动态选择计费策略
- Order类完全不知道具体费率实现,仅依赖抽象
2. 方法调用多态
// OrderItem构造函数中
this.rate = rateStrategy.calculateRate(chargeableWeight); // 动态绑定
- 同一方法调用根据实际对象类型产生不同行为
- 普通/加急/危险货物自动调用对应的费率计算方法
3. 集合处理多态
for (OrderItem item : orderItems) {
totalWeight += item.getChargeableWeight(); // 统一接口处理
}
- 通过父类/接口引用统一处理不同子类对象
三、继承与多态的协同效应
1. 订单处理流程
public Order(List<Cargo> cargos, Flight flight, calculateRate rateStrategy, ...)
- 构造函数接收接口类型参数,实现策略注入
- 后续所有计算都基于抽象接口操作
2. 客户折扣处理
if ("Corporate".equals(customer.getCustomerType())) {
totalPayment *= 0.8; // 多态判断
}
- 通过运行时类型判断实现差异化处理
3. 支付方式处理
switch (method) { // 类似多态的简化实现
case "Wechat" -> "微信";
case "ALiPay" -> "支付宝";
}
- 使用switch模拟多态行为(更优雅的实现可以进一步抽象)
四、我的反思心得:
1. 深化继承体系
abstract class Cargo { // 可建立货物基类
public abstract String getCargoType();
}
2. 增强多态应用
// 使用工厂方法创建费率策略
public static calculateRate createRateStrategy(String cargoType) {
return switch(cargoType) {
case "Expedite" -> new ExpediteRate();
case "Dangerous" -> new DangerousRate();
default -> new NormalRate();
};
}
3. 支付策略多态化
interface PaymentMethod {
String getPaymentInfo(double amount);
}
// 替代现有的字符串判断
这段代码充分展现了面向对象的核心思想:
- 通过继承建立类型体系
- 通过多态实现行为抽象
- 通过组合(如Order包含OrderItem)构建复杂对象
- 最终实现高内聚、低耦合的系统架构
那么其实在刚才的分析中,我们也提到了下一个关键词:
四:接口 (?)抽象类 设计与契约
罗老师上课曾这样说:接口,你就把他看成抽象类
为什么可以这样做呢?
我们来结合题集十:图形继承、多态、接口及集合框架分析一下:
一、抽象类在本项目中的应用
1. 抽象基类定义
abstract class AbstractShape implements Comparable<AbstractShape>{
public abstract double getArea();
public abstract void show();
public int compareTo(AbstractShape other){
return Double.compare(this.getArea(), other.getArea());
};
}
2. 核心作用
- 强制子类实现面积计算和显示功能
- 提供默认的比较逻辑(基于面积)
- 作为所有具体图形类的统一类型标识
3. 设计特点
- 包含抽象方法(getArea/show)定义契约
- 包含具体方法(compareTo)提供共享逻辑
- 实现Comparable接口增强比较能力
二、接口在本项目中的潜在应用
1. 当前设计的接口使用
- 仅通过实现Comparable接口提供比较功能
- 没有定义专门的图形功能接口
2. 可改进的接口设计
// 面积计算接口
interface AreaCalculable {
double getArea();
}
// 图形显示接口
interface Displayable {
void show();
}
// 抽象类可改为
abstract class AbstractShape implements AreaCalculable, Displayable, Comparable<AbstractShape>{
// 保持现有实现
}
3. 接口优势
- 解耦面积计算与图形显示的契约
- 允许非继承体系类实现这些接口
- 支持多接口组合实现更灵活的功能
三、抽象类与接口的关键区别
1. 抽象类特点
- 可以包含字段和具体方法实现
- 单继承限制(子类只能继承一个抽象类)
- 适合定义具有共同基础的类族核心行为
2. 接口特点
- 只能包含抽象方法和默认方法(Java 8+)
- 支持多实现(一个类可实现多个接口)
- 适合定义跨继承树的能力契约
3. 该题选择使用抽象类是因为:
* 需要共享compareTo实现
* 图形类有明显的继承关系
* 不需要多继承能力
- 可以补充接口来:
* 更清晰地分离功能契约
* 为未来扩展预留空间
四、反思与心得:
1. 抽象类适用场景
- 需要定义模板方法
- 多个相关类需要共享代码
- 需要控制子类构造过程
2. 接口适用场景
- 定义跨架构的能力
- 需要支持多重行为继承
- 定义轻量级服务契约
3. 组合使用建议
- 优先用接口定义行为契约
- 用抽象类提供基础实现
- 如本项目可:
* 保持现有抽象类结构
* 增加功能接口使设计更清晰
* 为特殊图形类保留实现多个接口的可能性
那么难道接口一定受制于抽象类,只是一个替代品吗?
我想并不是,比如这道题里面的接口,就是接入了java里面自带的排序comparable,而这个知识点在去年的期末考试题目有所体现:
我们有两种不同的排序接口,一种是默认的comparable:
在我们的ganary类中导入接口,再进程compareTo的方法重写:
再在主类里实现Collections的方法实现,完成排序,再直接循环打印就可以了:
comparable最终只会按照默认的升序来进行排序,得到最终的结果;
当然,如果面临不同情况的排序要求,我们可以使用更灵活的comparator进行排序;
这次不用在类里导入接口(匿名内部类),直接在主类里创建Comparator,定义比较方法后直接进行排序即可:
在:return Double.compare(o1.getVolume(), o2.getVolume());
这条语句做了以下工作:
获取o1的体积值:o1.getVolume()
获取o2的体积值:o2.getVolume()
使用Double.compare()静态方法比较两个double值
Double.compare(a, b)的返回值规则:
如果a < b,返回负数;
如果a == b,返回0;
如果a > b,返回正数;
如果需要不同的排序方式,只需创建不同的Comparator:
// 按高度排序
Comparator<Granary> heightComparator = new Comparator<Granary>() {
@Override
public int compare(Granary o1, Granary o2) {
return Double.compare(o1.getHeight(), o2.getHeight());
}
};
// 按类型排序
Comparator<Granary> typeComparator = new Comparator<Granary>() {
@Override
public int compare(Granary o1, Granary o2) {
return o1.getClass().getName().compareTo(o2.getClass().getName());
}
};
五:集合框架 数据管理的利器
Java集合框架的学习极大地提升了我的编程效率。通过实践,我深刻体会到了集合框架相比传统数组的巨大优势。
在我们的java实验第一次中,我们就已经接触到了这个得力助手:
而现在再回首,便有了更清晰的认识:
- 固定容量问题:数组需要预先确定大小,而集合可以动态增长
- 操作便利性:集合提供了丰富的API(add/remove/contains等)
- 类型安全:泛型集合避免了类型转换问题
而同样,在我们的期中考试的最后一题,对于王者荣耀里不同的英雄,我们最后也是通过集合的方式将他们搜罗起来:
class Team{
ArrayList<Hero> list=new ArrayList<>();
public Team() {}
public void joinTeam(Hero hero) {
list.add(hero);
}
public void sortHero(){
for (int i = 0; i < list.size()-1; i++) {
int minIndex = i; // 假设当前位置是最小值
// 在剩下的元素中找真正的最小值
for (int j = i+1; j < list.size(); j++) {
if (list.get(j).getHealth() < list.get(minIndex).getHealth()) {
minIndex = j;
}
}
// 将找到的最小值和当前位置交换
Hero temp = list.get(i);
list.set(i, list.get(minIndex));
list.set(minIndex, temp);
}
}
public void displaySortHero(){
for (int i = 0; i < list.size(); i++) {
System.out.printf("%s"+"("+"%.1f"+")"+" ",list.get(i).getName(),list.get(i).getHealth());
}
}
}
总结一下:
Java集合框架通过:
提供丰富的数据结构实现
简化常见操作
支持泛型和多态
与Stream API深度集成
极大地提高了开发效率和代码质量。从简单的动态数组到复杂的业务系统,集合框架都是Java开发者不可或缺的利器。掌握集合框架的合理使用,是成为合格Java开发者的重要一步。
那么谈到实际问题,就不得不提到:
六:异常处理 健壮程序的保障
异常处理机制的学习让我深刻认识到专业程序与业余脚本的本质区别。在PTA作业的作业过程中,我最初忽略了所有异常处理,导致程序经常在运行时报错,用户体验极差。这种经历让我明白:异常处理不是可选项,而是构建健壮程序的必备技能。
记得有一次,我的程序在读取文件时突然崩溃,控制台只留下一行晦涩的错误信息。我不得不花费整整两个小时,通过逐行调试的方式追踪问题根源——原来只是一个文件路径拼写错误。这种调试过程不仅效率低下,而且令人沮丧。
FileInputStream file = new FileInputStream("data.txt");
// 如果文件不存在,程序直接崩溃
// 改进后的专业写法
try {
FileInputStream file = new FileInputStream("data.txt");
// 文件操作逻辑
} catch (FileNotFoundException e) {
System.err.println("错误:未找到数据文件,请检查路径");
logger.error("文件读取失败", e);
} catch (IOException e) {
System.err.println("文件操作异常");
logger.error("IO异常", e);
}
异常处理带来的三大优势:
快速定位问题
良好的异常处理能直接指出错误类型和位置,相比无头苍蝇般的调试,效率提升十倍不止。通过合理的异常捕获和日志记录,我可以在数秒内定位到:
是文件权限问题?
网络连接超时?
还是内存不足?
程序自我修复能力
现在我会在关键操作中加入重试机制和备用方案:
Java
int retry = 0;
while(retry < 3) {
try {
connectToDatabase();
break;
} catch (SQLException e) {
retry++;
if(retry == 3) switchToBackupDB();
}
}
用户体验提升
不再是冷冰冰的堆栈跟踪,而是友好的错误提示:
[温馨提醒] 您要查看的图片暂时无法加载
(错误代码:404),建议:
1. 检查网络连接
2. 稍后再试
3. 联系客服(错误ID:XK2023)
反思与改进方向
虽然掌握了基础try-catch,但我仍存在明显不足:
异常分类模糊
对检查型异常(Checked Exception)和运行时异常(Runtime Exception)的使用场景仍会混淆。有时会不恰当地将业务异常继承自RuntimeException。
过度捕获问题
存在"catch(Exception e)"这种偷懒写法,掩盖了本应暴露的具体问题。正确的做法应该是精确捕获特定异常。
资源管理缺陷
对try-with-resources语法使用不够熟练,有时会忘记在finally块中关闭资源。
那么来到最后一个板块:
七:JavaFX 从命令行到图形界面
结合我们的javafx作业,我想自己有了如下收获与反思:
一、核心JavaFX组件应用与实现(结合翻转课堂上的相关学习)
1. 基础布局组件
VBox/HBox:构建了登录界面的垂直布局和按钮的水平布局
GridPane:精确组织了客户信息表单,实现标签和输入框的完美对齐
TabPane:创建了"新建订单"和"订单管理"两个功能标签页
ScrollPane:为长表单添加滚动功能,确保内容可完整浏览
2. 数据展示与交互
TableView:配置了4列展示货物ID、名称、重量和类型
ComboBox:实现省份选择下拉框,内置中国所有省份数据
TextField/PasswordField:定制了圆角边框和渐变背景的输入框
Button:设计了悬停效果的登录和添加货物按钮
3. 图形与动画
自定义矢量图形:使用Path和Polygon绘制了飞机图标
ImageView:加载云朵图片实现动态背景
Timeline动画:三种云朵漂浮效果(平移+透明度变化)
属性绑定:飞机图片宽度与面板宽度动态绑定
二、系统架构设计反思
1. MVC模式实现不足
虽然尝试遵循MVC模式,但在实际开发中发现:
控制器职责过重,处理了太多视图逻辑
模型与视图存在一定耦合,数据绑定不够彻底
缺少明确的视图更新机制
改进方案:
应引入更明确的观察者模式
public class OrderModel {
private List<ChangeListener> listeners = new ArrayList<>();
public void addListener(ChangeListener listener) {
listeners.add(listener);
}
private void notifyListeners() {
for (ChangeListener listener : listeners) {
listener.onChange();
}
}
}
2. 数据持久化问题
当前使用对象序列化存在:
数据格式不开放,难以与其他系统交互
缺少版本兼容性处理
异常处理不够完善
三、界面开发中的问题与改进
1. 样式管理混乱
现状:
样式直接写在Java代码中
重复样式定义
难以维护和修改
解决方案:
应提取到独立CSS文件
.button {
-fx-background-color: linear-gradient(to right, #0066cc, #0099ff);
-fx-text-fill: white;
-fx-font-weight: bold;
}
.text-field {
-fx-background-radius: 8;
-fx-border-radius: 8;
-fx-border-color: #b3e0ff;
}
2. 动画性能问题
实现云朵动画时发现:
同时运行多个Timeline影响性能
缺少硬件加速优化
动画参数调整困难
优化方案:
使用缓存和硬件加速
ImageView cloud = new ImageView();
cloud.setCache(true);
cloud.setCacheHint(CacheHint.SPEED);
四、项目经验总结与学习收获
1. 技术层面收获
掌握了JavaFX组件树构建方法
理解了属性绑定机制的优势
学会了基础动画实现原理
实践了数据持久化方案
2. 工程实践感悟
组件化思维:将UI拆分为可复用组件
异常处理:从崩溃到优雅恢复的转变
用户体验:微交互设计的重要性
代码可维护性:样式与逻辑分离的价值
3. 需要加强的方面
设计模式应用:需要更深入理解观察者、工厂等模式
性能优化:学习更多JavaFX性能调优技巧
测试覆盖:增加单元测试和UI自动化测试
响应式设计:掌握更专业的自适应布局技术
这个项目让我从命令行编程真正步入了GUI开发领域,过程中遇到的每个问题都是宝贵的学习机会。特别是认识到良好的异常处理和用户体验设计对专业软件的重要性。通过这次实践,我建立了GUI开发的整体认知框架,但也清楚看到自己在软件工程实践上的不足,这将成为我后续学习的重点方向
我想,纸上得来终觉浅,绝知此事要躬行!
🛫✈️🛬
这也是本学期的最后一次blog作业了,愿这次作业成为新的起点,让我们继续保持那份初见HelloWorld时的热忱,以严谨为舵,以创意为帆,在编程的星辰大海中,书写更多属于技术人的飞行日志。前路虽远,行则必至——这,便是程序员最浪漫的坚持。我们终将抵达青云,得意人生之志!(学长批改也辛苦啦^-^,祝期末门门高分!)