第三次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 goodsList)。印象很深的是:计算总重量和遍历货物进行费用计算时,ArrayList的迭代(for-each循环)和索引访问非常方便。后来在GUI项目中,也用HashMap(比如Map<String, User> userMap)来快速根据用户名查找用户对象,效率比遍历列表高得多。深刻感受到选对合适的集合容器对程序效率和简洁度提升巨大。

点击查看代码
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方面和集合框架方面还不是很了解,还需不断学习。
在此感谢面向对象课程的老师们良苦用心。

posted @ 2025-06-19 21:14  潇洒与浪荡  阅读(25)  评论(0)    收藏  举报