第三次Blog作业
前言
经过一学期的Java面向对象程序设计课程学习,我真正体会到了Java这门语言的强大之处和它的设计原理。课程安排挺有意思,通过PTA上的一个个编程挑战、动手的实验项目,再加上线上资源的自学和线下课堂的讨论与实践,构建了一套完整的学习闭环。这种模式让我对“面向对象”的理解从书本上的概念,逐渐变成了可以动手实现的代码逻辑。整体来看,工作量确实不小,尤其是一些PTA题目,经常需要花不少时间钻研调试,但带来的挑战感也让人很有收获感。课程的难度设置也比较合理,从最基础的语法开始,一步步深入到面向对象的核心特性(封装、继承、多态、抽象类/接口),最后再过渡到JavaFX的图形界面开发,这种循序渐进的过程让我的编程能力提升得很踏实。
面向对象技术总结
封装
通过PTA作业(比如那个模拟电梯调度系统)和实验项目,我深刻体会到了“封装”的意义。以前可能觉得private、public这些修饰符就是语法规则,现在才明白它们是如何实际地保护数据、控制访问边界,让代码更安全、更好维护的。踩过坑才知道它的好:记得在电梯项目初期,把一些内部状态(比如电梯的currentFloor)设成了public,结果在另一个处理类里不小心直接修改了它,导致电梯状态混乱,调试了半天才找到原因。后来老老实实用private加getter方法,并严格在Elevator类内部控制状态修改,问题就迎刃而解了。
点击查看代码
// 输入处理类
class InputProcessor {
private Scanner scanner;
public InputProcessor() {
this.scanner = new Scanner(System.in);
}
public List<String> getInput() {
List<String> inputList = new ArrayList<>();
String inputData = scanner.next();
while (!inputData.equalsIgnoreCase("End")) {
inputList.add(inputData);
inputData = scanner.next();
}
return inputList;
}
public void closeScanner() {
scanner.close();
}
}
// 电梯请求类
class ElevatorRequest {
private int floor;
private String direction;
public ElevatorRequest(int floor, String direction) {
this.floor = floor;
this.direction = direction;
}
public int getFloor() {
return floor;
}
public String getDirection() {
return direction;
}
}
// 电梯类
class Elevator {
private int minFloor;
private int maxFloor;
private int currentFloor = 1;
private String movingDirection;
private List<ElevatorRequest> externalCalls = new ArrayList<>();
private List<Integer> internalCalls = new ArrayList<>();
public Elevator(int minFloor, int maxFloor) {
this.minFloor = minFloor;
this.maxFloor = maxFloor;
}
public void addExternalCall(ElevatorRequest request) {
externalCalls.add(request);
}
public void addInternalCall(int floor) {
internalCalls.add(floor);
}
public List<ElevatorRequest> getExternalCalls() {
return externalCalls;
}
public List<Integer> getInternalCalls() {
return internalCalls;
}
public void move() {
if (currentFloor == 1) {
movingDirection = "UP";
}
while (!externalCalls.isEmpty() || !internalCalls.isEmpty()) {
processRequests();
}
}
private void processRequests() {
if (!internalCalls.isEmpty()) {
int targetFloor = internalCalls.get(0);
if (targetFloor > currentFloor) {
movingDirection = "UP";
} else {
movingDirection = "DOWN";
}
moveToFloor(targetFloor);
internalCalls.remove(0);
} else if (!externalCalls.isEmpty()) {
ElevatorRequest request = externalCalls.get(0);
int targetFloor = request.getFloor();
if (targetFloor > currentFloor) {
movingDirection = "UP";
} else {
movingDirection = "DOWN";
}
moveToFloor(targetFloor);
externalCalls.remove(0);
}
}
private void moveToFloor(int targetFloor) {
if (targetFloor > currentFloor) {
while (currentFloor < targetFloor) {
currentFloor++;
printCurrentStatus();
}
} else {
while (currentFloor > targetFloor) {
currentFloor--;
printCurrentStatus();
}
}
printDoorAction();
}
private void printCurrentStatus() {
System.out.println("Current Floor: " + currentFloor + " Direction: " + movingDirection);
}
private void printDoorAction() {
System.out.println("Open Door # Floor " + currentFloor);
System.out.println("Close Door");
}
}
封装知识点总结:
将数据(属性)和操作数据的方法(行为)封装在类中,通过访问修饰符(private/protected/public)控制外部访问,隐藏内部实现细节。
数据保护:通过private修饰属性,防止外部非法修改,保证数据安全性。简化接口:外部只需通过类提供的方法操作数据,无需关心内部实现细节
(如属性存储方式、计算逻辑)。代码复用与维护:封装后的类可独立修改内部逻辑,只要接口(方法签名)不变,外部调用不受影响。
继承
图形类体系(Shape -> Circle/Rectangle)的PTA作业是个很好的例子。实现了从基类Shape派生出Circle和Rectangle的过程,对extends关键字、方法重写(@Override)和用super调用父类构造方法/方法这些点掌握得更牢固了。不过,实践中也暴露了不足:对于更深层的多级继承(比如Shape->Polygon->Triangle)该如何合理设计,以及何时该用继承、何时该用组合(比如Car有一个Engine对象,而不是Car继承Engine),还需要在更复杂的项目中多练练手才能把握得更准。
点击查看代码
import java.util.ArrayList;
import java.util.Scanner;
abstract class Shape {
protected String shapeName;
public Shape() {
}
public Shape(String shapeName) {
this.shapeName = shapeName;
}
public String getShapeName() {
return shapeName;
}
public void setShapeName(String shapeName) {
this.shapeName = shapeName;
}
public abstract double getArea();
public abstract boolean validate();
public abstract void show();
}
class Circle extends Shape {
private double radius;
public Circle() {
super("Circle");
}
public Circle(double radius) {
super("Circle");
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public boolean validate() {
return radius > 0;
}
@Override
public void show() {
System.out.printf("Circle:%.2f ",this.getArea());
}
}
class Rectangle extends Shape {
private double width;
private double length;
public Rectangle() {
super("Rectangle");
}
public Rectangle(double width, double length) {
super("Rectangle");
this.width = width;
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
@Override
public double getArea() {
return width * length;
}
@Override
public boolean validate() {
return width > 0 && length > 0;
}
@Override
public void show() {
System.out.printf("Rectangle:%.2f ",this.getArea());
}
}
继承知识点总结:
继承是子类(派生类)获取父类(基类)的属性和方法,并可扩展新功能或重写父类方法的机制。Java 中通过extends关键字实现,且只支持单继承。
代码复用:子类无需重复实现父类已有的功能(如Person中的getName方法可直接被Student使用)。建立类的层次关系:通过继承形成 “is-a” 关系(如 “学生是一个人”),
使代码结构更清晰。
多态
在最后的GUI大作业里,我用接口回调实现了支付宝、微信、银行卡几种不同的支付方式。看着程序在运行时根据用户选择,动态地调用对应支付类的pay()方法完成支付,真正理解了“一个接口,多种实现”的精妙之处。不过我也意识到:目前的应用还比较直接,像策略模式(Strategy Pattern)这种更高级的、利用多态进行灵活行为组合的设计模式,在实际项目中运用起来还不够熟练,这也是后续需要重点学习的内容。
点击查看代码
// 创建订单界面
private void showNewOrderScene() {
VBox mainBox = new VBox(15);
mainBox.setPadding(new Insets(20));
mainBox.setStyle("-fx-background-color: #f0f8ff;");
Text title = new Text("创建新订单");
title.setFont(Font.font("Microsoft YaHei", 24));
title.setFill(Color.DARKBLUE);
mainBox.getChildren().add(title);
// 客户信息
GridPane clientForm = new GridPane();
clientForm.setHgap(10);
clientForm.setVgap(10);
clientForm.add(new Label("客户姓名:"), 0, 0);
TextField clientNameField = new TextField();
clientForm.add(clientNameField, 1, 0);
clientForm.add(new Label("电话:"), 0, 1);
TextField clientPhoneField = new TextField();
clientForm.add(clientPhoneField, 1, 1);
TitledPane clientPane = new TitledPane("客户信息", clientForm);
clientPane.setExpanded(true);
// 货物信息
GridPane cargoForm = new GridPane();
cargoForm.setHgap(10);
cargoForm.setVgap(10);
cargoForm.add(new Label("货物名称:"), 0, 0);
TextField cargoNameField = new TextField();
cargoForm.add(cargoNameField, 1, 0);
cargoForm.add(new Label("类型:"), 0, 1);
ComboBox<String> cargoTypeCombo = new ComboBox<>();
cargoTypeCombo.getItems().addAll("普通货物", "危险货物", "加急货物");
cargoTypeCombo.setValue("普通货物");
cargoForm.add(cargoTypeCombo, 1, 1);
cargoForm.add(new Label("重量(kg):"), 0, 2);
TextField weightField = new TextField();
cargoForm.add(weightField, 1, 2);
cargoForm.add(new Label("长(cm):"), 0, 3);
TextField lengthField = new TextField();
cargoForm.add(lengthField, 1, 3);
cargoForm.add(new Label("宽(cm):"), 0, 4);
TextField widthField = new TextField();
cargoForm.add(widthField, 1, 4);
cargoForm.add(new Label("高(cm):"), 0, 5);
TextField heightField = new TextField();
cargoForm.add(heightField, 1, 5);
TitledPane cargoPane = new TitledPane("货物信息", cargoForm);
cargoPane.setExpanded(true);
// 运输信息
GridPane shippingForm = new GridPane();
shippingForm.setHgap(10);
shippingForm.setVgap(10);
shippingForm.add(new Label("发件人:"), 0, 0);
TextField senderNameField = new TextField();
shippingForm.add(senderNameField, 1, 0);
shippingForm.add(new Label("电话:"), 0, 1);
TextField senderPhoneField = new TextField();
shippingForm.add(senderPhoneField, 1, 1);
shippingForm.add(new Label("地址:"), 0, 2);
TextField senderAddressField = new TextField();
shippingForm.add(senderAddressField, 1, 2);
shippingForm.add(new Label("收件人:"), 0, 3);
TextField receiverNameField = new TextField();
shippingForm.add(receiverNameField, 1, 3);
shippingForm.add(new Label("电话:"), 0, 4);
TextField receiverPhoneField = new TextField();
shippingForm.add(receiverPhoneField, 1, 4);
shippingForm.add(new Label("地址:"), 0, 5);
TextField receiverAddressField = new TextField();
shippingForm.add(receiverAddressField, 1, 5);
shippingForm.add(new Label("航班号:"), 0, 6);
ComboBox<String> flightCombo = new ComboBox<>();
flightCombo.getItems().addAll(flights.stream().map(Flight::getNumber).collect(Collectors.toList()));
if (!flights.isEmpty()) flightCombo.setValue(flights.get(0).getNumber());
shippingForm.add(flightCombo, 1, 6);
TitledPane shippingPane = new TitledPane("运输信息", shippingForm);
shippingPane.setExpanded(true);
// 支付方式
GridPane paymentForm = new GridPane();
paymentForm.setHgap(10);
paymentForm.setVgap(10);
ToggleGroup paymentGroup = new ToggleGroup();
RadioButton alipayBtn = new RadioButton("支付宝支付");
alipayBtn.setToggleGroup(paymentGroup);
RadioButton wechatBtn = new RadioButton("微信支付");
wechatBtn.setToggleGroup(paymentGroup);
RadioButton cashBtn = new RadioButton("现金支付");
cashBtn.setToggleGroup(paymentGroup);
alipayBtn.setSelected(true);
paymentForm.add(alipayBtn, 0, 0);
paymentForm.add(wechatBtn, 1, 0);
paymentForm.add(cashBtn, 2, 0);
TitledPane paymentPane = new TitledPane("支付方式", paymentForm);
paymentPane.setExpanded(true);
mainBox.getChildren().addAll(clientPane, cargoPane, shippingPane, paymentPane);
HBox buttonBox = new HBox(20);
buttonBox.setAlignment(Pos.CENTER);
Button saveBtn = new Button("保存订单");
saveBtn.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
Button cancelBtn = new Button("取消");
cancelBtn.setStyle("-fx-background-color: #f44336; -fx-text-fill: white; -fx-font-size: 14px;");
buttonBox.getChildren().addAll(saveBtn, cancelBtn);
saveBtn.setOnAction(e -> {
try {
// 创建货物
double weight = Double.parseDouble(weightField.getText());
double length = Double.parseDouble(lengthField.getText());
double width = Double.parseDouble(widthField.getText());
double height = Double.parseDouble(heightField.getText());
String cargoType = cargoTypeCombo.getValue();
Cargo cargo = new Cargo(cargoNameField.getText(), length, width, height, weight, cargoType);
// 创建订单
Order order = new Order();
order.setClient(currentUser);
order.getCargos().add(cargo);
order.setSenderName(senderNameField.getText());
order.setSenderPhone(senderPhoneField.getText());
order.setSenderAddress(senderAddressField.getText());
order.setReceiverName(receiverNameField.getText());
order.setReceiverPhone(receiverPhoneField.getText());
order.setReceiverAddress(receiverAddressField.getText());
order.setFlightNumber(flightCombo.getValue());
// 设置支付方式
RadioButton selected = (RadioButton) paymentGroup.getSelectedToggle();
order.setPaymentMethod(selected.getText());
// 计算运费
order.setAmount(order.calculateTotal());
orders.add(order);
saveOrders();
showAlert("订单创建成功", "订单已成功保存,订单号: " + order.getId());
showMainScene();
} catch (NumberFormatException ex) {
showAlert("输入错误", "请输入有效的数字值");
}
});
cancelBtn.setOnAction(e -> showMainScene());
mainBox.getChildren().add(buttonBox);
ScrollPane scrollPane = new ScrollPane(mainBox);
scrollPane.setFitToWidth(true);
Scene scene = new Scene(scrollPane, 800, 600);
primaryStage.setScene(scene);
}
多态知识点总结:
多态指同一个方法调用在不同的对象上会产生不同的行为,分为编译时多态(方法重载,Overload)和运行时多态(方法重写,Override)。
方法重载:同一类中多个方法名相同,但参数列表(类型、顺序、数量)不同,编译时根据参数匹配方法。
方法重写:子类重新实现父类的方法(方法名、参数、返回值完全一致),运行时根据对象实际类型调用对应方法。
灵活性与扩展性:代码可通过父类引用处理不同子类对象,无需为每个子类单独编写逻辑(如上述例子中用Animal引用调用Dog的方法)。
接口统一:多态使不同类对象可通过相同接口交互,降低代码耦合度(如集合框架中List接口可存储所有实现类对象)。
抽象类和接口
这两者看起来有点相似,但在实践中才真正摸清了它们的适用场景。抽象类(abstract class)更像是一个“不完整的模板”,比如定义一个抽象的Animal类,包含所有动物共有的属性(name, weight)和行为模板(call()),但call()的具体声音留给子类实现。而接口(interface),比如Aggressive,则纯粹定义一种“契约”或“能力” (isAggressive()),任何类(无论它是什么继承体系的)只要实现了这个接口,就具备了这种能力。
大象装冰箱的实验让我对这两者的区别和应用有了切身体会:Animal作为抽象类提供共性基础,Aggressive接口则给特定动物(狮子、老虎)打上了“有攻击性”的标签,使得后续的排序、战斗逻辑可以基于这个接口进行,设计上更清晰灵活。
点击查看代码
// 定义有攻击性接口
interface Aggressive {
int isAggressive();
}
// 抽象动物类,实现Comparable接口
abstract class Animal implements Comparable<Animal> {
protected String name;
protected int weight;
protected int enterOrder;
public Animal(String name, int weight, int enterOrder) {
this.name = name;
this.weight = weight;
this.enterOrder = enterOrder;
}
public void call() {
}
public int getWeight() {
return weight;
}
public String getName() {
return name;
}
public int getEnterOrder() {
return enterOrder;
}
// 实现Comparable接口的compareTo方法,比较战斗力
@Override
public int compareTo(Animal another) {
Aggressive selfAggressive = (Aggressive) this;
Aggressive anotherAggressive = (Aggressive) another;
int selfAttack = selfAggressive.isAggressive();
int anotherAttack = anotherAggressive.isAggressive();
if (selfAttack > anotherAttack) {
return 1;
} else if (selfAttack < anotherAttack) {
return -1;
} else {
return Integer.compare(this.weight, another.weight);
}
}
}
// 大象类,实现Aggressive接口
class Elephant extends Animal implements Aggressive {
public Elephant(String name, int weight, int enterOrder) {
super(name, weight, enterOrder);
}
@Override
public void call() {
System.out.println(name + ":咆哮一声");
}
@Override
public int isAggressive() {
return 0;
}
}
// 狮子类,实现Aggressive接口
class Lion extends Animal implements Aggressive {
public Lion(String name, int weight, int enterOrder) {
super(name, weight, enterOrder);
}
@Override
public void call() {
System.out.println(name + ":怒吼一声");
}
@Override
public int isAggressive() {
return 1;
}
}
// 老虎类,实现Aggressive接口
class Tiger extends Animal implements Aggressive {
public Tiger(String name, int weight, int enterOrder) {
super(name, weight, enterOrder);
}
@Override
public void call() {
System.out.println(name + ":嗷嗷一声");
}
@Override
public int isAggressive() {
return 1;
}
}
抽象类和接口知识点总结:
抽象类是使用abstract关键字修饰的类,它不能直接实例化,只能被继承。抽象类中可包含抽象方法(仅有声明,没有实现,需用abstract修饰)
和普通方法。
规范子类行为:抽象类定义了子类必须实现的方法(如getArea),确保子类遵循统一的接口规范。代码复用与扩展:抽象类中可包含通用逻辑(如display方法),
子类继承后直接使用或扩展。
接口是使用interface关键字定义的抽象类型,其中所有方法默认是public abstract(可省略修饰符),所有属性默认是public static final。
接口支持多实现(一个类可实现多个接口)。
解耦与模块化:接口定义 “能做什么”,而不关心 “如何做”,实现类可独立开发,多实现机制:弥补 Java 单继承的限制,使类可同时具备多种行为
集合框架
在航空货运管理系统的PTA作业里,ArrayList被用来灵活地存储和管理货物列表(List
点击查看代码
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取客户信息
String clientID = scanner.nextLine();
String clientName = scanner.nextLine();
String clientPhone = scanner.nextLine();
String clientAddress = scanner.nextLine();
int goodsNumber = Integer.parseInt(scanner.nextLine());
List<Goods> goodsList = new ArrayList<>();
for (int i = 0; i < goodsNumber; i++) {
String goodsID = scanner.nextLine();
String goodsName = scanner.nextLine();
double width = Double.parseDouble(scanner.nextLine());
double length = Double.parseDouble(scanner.nextLine());
double height = Double.parseDouble(scanner.nextLine());
double weight = Double.parseDouble(scanner.nextLine());
Goods goods = new Goods(goodsID, goodsName, width, length, height, weight);
goodsList.add(goods);
}
// 读取航班信息
String flightID = scanner.nextLine();
String departure = scanner.nextLine();
String arrival = scanner.nextLine();
String flightDate = scanner.nextLine();
double maxWeight = Double.parseDouble(scanner.nextLine());
// 读取订单信息
String orderID = scanner.nextLine();
String orderDate = scanner.nextLine();
String senderAddress = scanner.nextLine();
String senderName = scanner.nextLine();
String senderPhone = scanner.nextLine();
String receiverAddress = scanner.nextLine();
String receiverName = scanner.nextLine();
String receiverPhone = scanner.nextLine();
// 计算每个货物的计费重量和总重量
double totalWeight = 0.0;
List<Double> chargeWeights = new ArrayList<>();
for (Goods goods : goodsList) {
double volumeWeight = (goods.getLength() * goods.getWidth() * goods.getHeight()) / 6000;
double chargeWeight = Math.max(volumeWeight, goods.getWeight());
chargeWeights.add(chargeWeight);
totalWeight += chargeWeight;
}
// 判断航班载重量
if (totalWeight > maxWeight) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n", flightID);
return;
}
// 输出客户订单信息
System.out.println("客户:" + clientName + "(" + clientPhone + ")订单信息如下:");
System.out.println("-----------------------------------------");
System.out.println("航班号:" + flightID);
System.out.println("订单号:" + orderID);
System.out.println("订单日期:" + orderDate);
System.out.println("发件人姓名:" + senderName);
System.out.println("发件人电话:" + senderPhone);
System.out.println("发件人地址:" + senderAddress);
System.out.println("收件人姓名:" + receiverName);
System.out.println("收件人电话:" + receiverPhone);
System.out.println("收件人地址:" + receiverAddress);
System.out.printf("订单总重量(kg):%.1f\n", totalWeight);
// 计算每个货物的费率和应交运费,以及总支付金额
double totalPayment = 0.0;
List<Double> rates = new ArrayList<>();
List<Double> payments = new ArrayList<>();
for (int i = 0; i < chargeWeights.size(); i++) {
double chargeWeight = chargeWeights.get(i);
double rate;
if (chargeWeight < 20) {
rate = 35;
} else if (chargeWeight < 50) {
rate = 30;
} else if (chargeWeight < 100) {
rate = 25;
} else {
rate = 15;
}
rates.add(rate);
double payment = chargeWeight * rate;
payments.add(payment);
totalPayment += payment;
}
System.out.printf("微信支付金额:%.1f\n", totalPayment);
System.out.printf("\n");
// 输出货物明细
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
for (int i = 0; i < goodsList.size(); i++) {
Goods goods = goodsList.get(i);
System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n", i + 1, goods.getName(), chargeWeights.get(i), rates.get(i), payments.get(i));
}
}
}
集合框架知识点总结:
Java 集合框架是一组用于存储和操作数据的接口、实现类和工具类,位于java.util包中。核心接口包括:
Collection:单列集合顶层接口,分为List(有序、可重复)和Set(无序、不可重复)。
Map:双列集合,存储键值对(Key-Value)。
高效数据存储:根据不同需求选择合适的集合(如需要有序用TreeSet,需要快速查询用HashMap)
异常处理
这部分的学习是个“血泪史”,但也养成了好习惯。实验四(动物的随机创建)里,文件读取(FileReader)、类型转换(parseInt)、空值判断(Objects.requireNonNull)、数组越界、资源关闭等等,处处都可能“爆雷”。刚开始总是忽略异常处理,程序动不动就崩溃。后来强迫自己用try-catch-finally(以及更优雅的try-with-resources)把可能出现问题的地方包裹起来,并给出有意义的错误信息,程序的健壮性一下子就上来了。不过,目前主要还是用Java内置的异常类,对于在特定业务场景下(比如自定义的业务规则校验失败)创建自己的异常类型(MyValidationException),经验还比较少,需要在后续项目中实践。
点击查看代码
public class Main {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
// 读取并验证冰箱信息
String iceBoxLine = Objects.requireNonNull(reader.readLine(), "冰箱信息不能为空");
String[] iceBoxInput = iceBoxLine.split(" ");
validateInput(iceBoxInput, "冰箱", 2);
IceBox iceBox = new IceBox(iceBoxInput[0], parseInt(iceBoxInput[1], "冰箱容量"));
// 读取并验证洗衣机信息
String washingMachineLine = Objects.requireNonNull(reader.readLine(), "洗衣机信息不能为空");
String[] washingMachineInput = washingMachineLine.split(" ");
validateInput(washingMachineInput, "洗衣机", 2);
WashingMachine washingMachine = new WashingMachine(
washingMachineInput[0],
parseInt(washingMachineInput[1], "洗衣机容量")
);
// 读取并验证微波炉信息
String microwaveLine = Objects.requireNonNull(reader.readLine(), "微波炉信息不能为空");
String[] microwaveInput = microwaveLine.split(" ");
validateInput(microwaveInput, "微波炉", 2);
Microwave microwave = new Microwave(
microwaveInput[0],
parseInt(microwaveInput[1], "微波炉容量")
);
// 计算总容量(防止溢出)
long totalCapacityLong = (long) iceBox.capacity + washingMachine.capacity + microwave.capacity;
if (totalCapacityLong > Integer.MAX_VALUE) {
throw new ArithmeticException("总容量超过整数最大值: " + totalCapacityLong);
}
int totalCapacity = (int) totalCapacityLong;
// 生成并添加随机动物
Random rand = new SecureRandom();
for (int i = 0; i < totalCapacity; i++) {
Animal animal = createRandomAnimal(i, rand);
int electricType = rand.nextInt(3); // 0-冰箱, 1-洗衣机, 2-微波炉
switch (electricType) {
case 0 -> iceBox.enterAnimal(animal);
case 1 -> washingMachine.enterAnimal(animal);
case 2 -> microwave.enterAnimal(animal);
default -> throw new AssertionError("无效的电器类型: " + electricType);
}
}
// 展示结果(每个展示单独异常处理)
safeSortAndShow(iceBox, "冰箱");
safeSortAndShow(washingMachine, "洗衣机");
safeSortAndShow(microwave, "微波炉");
// 按动物数量排序电器
List<Electric> electrics = new ArrayList<>();
Collections.addAll(electrics, iceBox, washingMachine, microwave);
Collections.sort(electrics, new ElectricComparator());
System.out.println("===== 按动物数量对电器排序结果 =====");
for (Electric electric : electrics) {
System.out.println(electric.name + " 中动物数量: " +
Objects.requireNonNull(electric.animals, "动物列表未初始化").size());
}
} catch (FileNotFoundException e) {
System.err.println("错误: 输入文件未找到 - " + e.getMessage());
} catch (IOException e) {
System.err.println("I/O错误: " + e.getMessage());
} catch (NullPointerException | IllegalArgumentException | ArithmeticException |
IllegalStateException e) {
System.err.println("数据处理错误: " + e.getMessage());
} catch (Exception e) {
System.err.println("未知错误: " + e.getMessage());
e.printStackTrace();
}
}
JavaFX
图形界面开发部分采用了学生自主讲解的形式,我负责的是FlowPane布局面板的讲解和实践。FlowPane的特点就是“流式布局”,控件(比如按钮)会按顺序排列,一行(或一列)放满了就自动“流”到下一行(列)。在准备讲解材料时,我重点研究了它的核心属性:Orientation(水平HORIZONTAL/垂直VERTICAL)、hgap/vgap(控件间距)、alignment(整体对齐方式)。写了一个简单的Demo程序,动态添加多个按钮,演示了不同属性设置下控件的排列效果。通过这次准备,对FlowPane的适用场景(比如工具栏、标签云、图片墙)和使用细节有了更直观深入的理解。
点击查看代码
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Button b1 = new Button("button1");
Button b2 = new Button("button2");
Button b3 = new Button("button3");
Button b4 = new Button("button4");
Button b5 = new Button("button5");
Button b6 = new Button("button6");
Button b7 = new Button("button7");
Button b8 = new Button("button8");
FlowPane flow = new FlowPane();
flow.setStyle("-fx-background-color:#EE6AA7");
flow.setAlignment(Pos.CENTER);
flow.setHgap(10);
flow.setVgap(10);
flow.setOrientation(Orientation.VERTICAL);
flow.getChildren().addAll(b1,b2,b3,b4,b5,b6,b7,b8);
Scene scene = new Scene(flow);
primaryStage.setScene(scene);
primaryStage.setTitle("javafx");
primaryStage.setWidth(800);
primaryStage.setHeight(800);
primaryStage.show();
}
}
采坑心得
对象引用传递的误解:
刚开始犯过一个低级但典型的错误:以为把一个对象赋值给另一个变量是“复制”了一份。结果修改新变量时,原对象也跟着变了!后来才明白,这操作只是复制了引用,指向的还是同一个对象。解决之道:需要真正的对象拷贝时,要么实现Cloneable接口并重写clone()方法(要小心深浅拷贝问题),要么老老实实写一个“复制构造函数”。
接口设计问题:
在最初设计接口时,总想让它“包罗万象”,一个接口塞进去十几个方法。结果实现类被迫实现一堆可能根本用不到的方法,代码臃肿且难以理解。教训深刻:后来学习了“接口隔离原则”(ISP),明白了接口应该“小而专”。比如支付系统,把一个大Payment接口拆成了OnlinePayment(包含payOnline(), refundOnline())、CardPayment(包含swipeCard(), enterPIN())等更聚焦的小接口,清晰多了。
集合迭代中的修改:
有一次在遍历一个ArrayList (for (String item : myList))的过程中,想直接myList.remove(item)删除某个元素,结果程序立马抛出了ConcurrentModificationException异常,一脸懵。终于搞懂:不能在增强型for循环或迭代器遍历过程中直接用集合的remove/add方法修改集合结构。可靠方法:要么使用Iterator的remove()方法;要么改用CopyOnWriteArrayList这种线程安全的集合(适用于并发不高的场景);要么先收集要删除的元素,遍历结束后再批量删除。
资源释放遗漏:
写数据库操作或者文件读写时,经常开了Connection、FileInputStream,结果忘了关!虽然小程序可能看不出问题,但这绝对是隐患(资源泄漏)。最佳实践:try-with-resources语法,只要资源类实现了AutoCloseable接口(JDBC连接、各种流都实现了),把它写在try后面的括号里 (try (Connection conn = ...; FileInputStream fis = ...)),无论是否发生异常,finally里的关闭操作都会自动执行,省心又安全。
改进建议和总结
总体来说这门课收获颇丰,在我面前打开了面向对象的大门,这门课的内容和作业等安排让我学得十分的充实,就是在JavaFx方面和集合框架方面还不是很了解,还需不断学习。
在此感谢面向对象课程的老师们良苦用心。

浙公网安备 33010602011771号