题目集8~9的总结
前言:
本次题目集八相较于题目集九要偏简单。题目集八点与面的设计是在题目集六的基础上多加了继承和多态,而题目集九则是在八的基础上对题目的类设计进行重构,增加容器类保存点、线、面对象,并对该容器进行相应增、删、遍历操作,加大了题目的难度。题目集八主要考察的是继承与多态的问题,而航空题目的部分则是以类的设计为主,并不需要什么太多的算法,这需要满足单一职责原则,里氏代换原则,开闭原则,合成复用原则即可。题目集九则是在继承与多态的知识点上还需要用到抽象类等知识。每次的题目集中都有前几次题目集当中的迭代问题,题目量虽然不是很大,但是每一道题目都需要我们认真的去思考。题目给了我们类图,这样就让我们在写题目的时候就可以轻松许多,有了参考,以及该往哪方面去想,使我们的思维更加缜密。在难度方面,我个人认为这一次的迭代问题比上一次要简单一点,上一次需要考虑的问题偏多,没有什么经验,这一次的迭代就比上一次就要轻松一些了。
设计与分析:
我们首先来看一下点与线的:
下面是题目八的
点击查看代码
import java.util.Scanner;
// 抽象类Solid
abstract class Solid {
protected double side;
public Solid() {}
public Solid(double side) {
this.side = side;
}
public double getSide() {
return side;
}
public void setSide(double side) {
this.side = side;
}
public abstract double getArea();
public abstract double getVolume();
}
// 正方体类Cube,继承自Solid
class Cube extends Solid {
public Cube() {}
public Cube(double side) {
super(side);
}
@Override
public double getArea() {
return 6 * side * side;
}
@Override
public double getVolume() {
return side * side * side;
}
}
// 正棱锥类RegularPyramid,继承自Solid
class RegularPyramid extends Solid {
public RegularPyramid() {}
public RegularPyramid(double side) {
super(side);
}
// 正三棱锥底面为正三角形,面积公式:sqrt(3) / 4 * a^2 ,这里a为边长
// 侧面积每个三角形面积为:sqrt(3) / 4 * a^2 ,正三棱锥有3个侧面
// 总表面积 = 底面积 + 侧面积 = 4 * sqrt(3) / 4 * a^2 = sqrt(3) * a^2
@Override
public double getArea() {
return Math.sqrt(3) * side * side;
}
// 正三棱锥体积公式:sqrt(2) / 12 * a^3 ,这里a为边长
@Override
public double getVolume() {
return Math.sqrt(2) / 12 * side * side * side;
}
}
// 抽象类RubikCube
abstract class RubikCube {
protected String color;
protected int layer;
protected Solid solid;
public RubikCube() {}
public RubikCube(String color, int layer, Solid solid) {
this.color = color;
this.layer = layer;
this.solid = solid;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getLayer() {
return layer;
}
public void setLayer(int layer) {
this.layer = layer;
}
public Solid getSolid() {
return solid;
}
public void setSolid(Solid solid) {
this.solid = solid;
}
public abstract double getArea();
public abstract double getVolume();
}
// 正方体魔方类SquareCube,继承自RubikCube
class SquareCube extends RubikCube {
public SquareCube() {}
public SquareCube(String color, int layer, Solid solid) {
super(color, layer, solid);
}
@Override
public double getArea() {
// 魔方边长 = 阶数 * 单元边长
double sideLength = layer * solid.getSide();
return solid.getArea() * layer * layer;
}
@Override
public double getVolume() {
double sideLength = layer * solid.getSide();
return solid.getVolume() * layer * layer * layer;
}
}
// 正三棱锥魔方类RegularPyramidCube,继承自RubikCube
class RegularPyramidCube extends RubikCube {
public RegularPyramidCube() {}
public RegularPyramidCube(String color, int layer, Solid solid) {
super(color, layer, solid);
}
@Override
public double getArea() {
double sideLength = layer * solid.getSide();
return solid.getArea() * layer * layer;
}
@Override
public double getVolume() {
double sideLength = layer * solid.getSide();
return solid.getVolume() * layer * layer * layer;
}
}
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String color = input.next();
int layer = input.nextInt();
double side = input.nextDouble();
RubikCube cube1 = new SquareCube(color, layer, new Cube(side));
color = input.next();
layer = input.nextInt();
side = input.nextDouble();
RubikCube cube2 = new RegularPyramidCube(color, layer, new RegularPyramid(side));
display(cube1);
display(cube2);
}
public static void display(RubikCube cube) {
System.out.println(cube.getColor());
System.out.printf("%.2f\n", cube.getArea());
System.out.printf("%.2f\n", cube.getVolume());
}
}
点击查看代码
import java.util.Scanner;
// 抽象类Element
abstract class Element {
public abstract void display();
}
// 点类Point,继承自Element
class Point extends Element {
private double x;
private double y;
public Point() {}
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
@Override
public void display() {
System.out.println("(" + String.format("%.2f", x) + "," + String.format("%.2f", y) + ")");
}
}
// 线类Line,继承自Element
class Line extends Element {
private Point point1;
private Point point2;
private String color;
public Line() {}
public Line(Point p1, Point p2, String color) {
this.point1 = p1;
this.point2 = p2;
this.color = color;
}
public Point getPoint1() {
return point1;
}
public void setPoint1(Point point1) {
this.point1 = point1;
}
public Point getPoint2() {
return point2;
}
public void setPoint2(Point point2) {
this.point2 = point2;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getDistance() {
double dx = point2.getX() - point1.getX();
double dy = point2.getY() - point1.getY();
return Math.sqrt(dx * dx + dy * dy);
}
@Override
public void display() {
System.out.println("The line's color is:" + color);
System.out.println("The line's begin point's Coordinate is:");
point1.display();
System.out.println("The line's end point's Coordinate is:");
point2.display();
System.out.println("The line's length is:" + String.format("%.2f", getDistance()));
}
}
// 面类Plane,继承自Element
class Plane extends Element {
private String color;
public Plane() {}
public Plane(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void display() {
System.out.println("The Plane's color is:" + color);
}
}
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
double x1 = input.nextDouble();
double y1 = input.nextDouble();
double x2 = input.nextDouble();
double y2 = input.nextDouble();
String color = input.next();
Point p1 = new Point(x1, y1);
Point p2 = new Point(x2, y2);
Line line = new Line(p1, p2, color);
Plane plane = new Plane(color);
Element element;
element = p1;
element.display();
element = p2;
element.display();
element = line;
element.display();
element = plane;
element.display();
} catch (Exception e) {
System.out.println("Wrong Format");
}
}
}
方法设计
- display()方法:在抽象类
Element中声明为抽象方法,各个子类重写该方法实现各自特定的展示逻辑。这种设计符合多态的定义,即同一方法名在不同对象上有不同的行为表现。通过父类引用调用display()方法时,能根据实际指向的子类对象执行相应的展示代码,增强了代码的灵活性和可读性。 - 其他方法:如
Point类的属性访问器和修改器方法,Line类的getDistance()方法等,这些方法都是围绕类所代表的几何对象的属性和行为来设计,遵循了面向对象中数据封装和模块化的原则,将对象的操作封装在类内部,外部通过接口(方法)进行访问和交互。
功能分析
-
输入处理:通过
Scanner获取用户输入,在Main类的main方法中进行输入值的读取和对象创建。同时使用try - catch块捕获可能的输入异常,当输入格式不符合要求(如输入非数值类型等)时,能友好地提示Wrong Format,保证了程序的健壮性。 -
多态实现:利用
Element类的引用分别指向不同子类对象(Point、Line、Plane),然后调用display()方法。这样在运行时,根据实际对象类型执行对应的display()实现代码,实现了多态特性。例如,同样是调用display()方法,Point对象展示坐标,Line对象展示线的综合信息,Plane对象展示面的颜色,体现了多态在代码复用和灵活性方面的优势。 -
几何信息处理:
Point类准确表示点坐标,Line类通过计算两点间距离得到线段长度,在展示时能完整输出几何对象的相关属性信息,满足了对“点与线”相关几何信息展示的功能需求。
![]()
接下来我们来看魔方的:
点击查看代码
import java.util.Scanner;
abstract class Solid {
protected double side;
public Solid() {}
public Solid(double side) {
this.side = side;
}
public double getSide() {
return side;
}
public void setSide(double side) {
this.side = side;
}
public abstract double getArea();
public abstract double getVolume();
}
class Cube extends Solid {
public Cube() {}
public Cube(double side) {
super(side);
}
@Override
public double getArea() {
return 6 * side * side;
}
@Override
public double getVolume() {
return side * side * side;
}
}
class RegularPyramid extends Solid {
public RegularPyramid() {}
public RegularPyramid(double side) {
super(side);
}
@Override
public double getArea() {
return Math.sqrt(3) * side * side;
}
@Override
public double getVolume() {
return Math.sqrt(2) / 12 * side * side * side;
}
}
abstract class RubikCube {
protected String color;
protected int layer;
protected Solid solid;
public RubikCube() {}
public RubikCube(String color, int layer, Solid solid) {
this.color = color;
this.layer = layer;
this.solid = solid;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getLayer() {
return layer;
}
public void setLayer(int layer) {
this.layer = layer;
}
public Solid getSolid() {
return solid;
}
public void setSolid(Solid solid) {
this.solid = solid;
}
public abstract double getArea();
public abstract double getVolume();
}
class SquareCube extends RubikCube {
public SquareCube() {}
public SquareCube(String color, int layer, Solid solid) {
super(color, layer, solid);
}
@Override
public double getArea() {
double sideLength = layer * solid.getSide();
return solid.getArea() * layer * layer;
}
@Override
public double getVolume() {
double sideLength = layer * solid.getSide();
return solid.getVolume() * layer * layer * layer;
}
}
class RegularPyramidCube extends RubikCube {
public RegularPyramidCube() {}
public RegularPyramidCube(String color, int layer, Solid solid) {
super(color, layer, solid);
}
@Override
public double getArea() {
double sideLength = layer * solid.getSide();
return solid.getArea() * layer * layer;
}
@Override
public double getVolume() {
double sideLength = layer * solid.getSide();
return solid.getVolume() * layer * layer * layer;
}
}
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String color = input.next();
int layer = input.nextInt();
double side = input.nextDouble();
RubikCube cube1 = new SquareCube(color, layer, new Cube(side));
color = input.next();
layer = input.nextInt();
side = input.nextDouble();
RubikCube cube2 = new RegularPyramidCube(color, layer, new RegularPyramid(side));
display(cube1);
display(cube2);
}
public static void display(RubikCube cube) {
System.out.println(cube.getColor());
System.out.printf("%.2f\n", cube.getArea());
System.out.printf("%.2f\n", cube.getVolume());
}
}
功能设计分析
- 输入处理:在
Main类的main方法中,使用Scanner类从键盘读取用户输入的魔方颜色、阶数和单元边长信息。通过依次读取不同类型的输入值,为创建相应的魔方对象提供必要的数据,确保程序能够根据用户的输入构建出准确的魔方实例。 - 多态实现:利用
RubikCube类的引用分别指向SquareCube和RegularPyramidCube对象,然后调用display方法。在display方法中,根据实际指向的魔方对象类型,调用对应的getArea()和getVolume()方法,实现了多态输出。这种多态设计使得程序在处理不同类型魔方时更加灵活,代码结构更加清晰,也方便后续对不同类型魔方进行扩展和维护。 - 魔方属性与计算:程序准确地处理了魔方的三个属性(颜色、阶数、类型),并根据魔方的类型(正方体魔方或正三棱锥魔方),结合其对应的立体图形(正方体或正三棱锥)的几何特性,计算出魔方的表面积和体积。在计算过程中,考虑到魔方边长与阶数、单元边长的关系,通过合理的公式应用,保证了计算结果的准确性,最终按照要求的格式输出魔方的颜色、表面积和体积。
最后来看航空货运管理系统的:
这是题目八的:
点击查看代码
import java.util.*;
// 支付接口
interface Payment {
String pay(double amount);
}
// 微信支付实现
class WeChatPayment implements Payment {
@Override
public String pay(double amount) {
return String.format("微信支付金额:%.1f", amount);
}
}
// 支付宝支付实现
class AliPayPayment implements Payment {
@Override
public String pay(double amount) {
return String.format("支付宝支付金额:%.1f", amount);
}
}
// 现金支付实现
class CashPayment implements Payment {
@Override
public String pay(double amount) {
return String.format("现金支付金额:%.1f", amount);
}
}
// 货物接口
interface Cargo {
double calculateChargeableWeight();
double calculateBaseFee();
String getName();
double getWeight();
}
// 普通货物
class NormalCargo implements Cargo {
private String id;
private String name;
private double length;
private double width;
private double height;
private double weight;
public NormalCargo(String id, String name, double length, double width, double height, double weight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
@Override
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
@Override
public double calculateBaseFee() {
double chargeableWeight = calculateChargeableWeight();
if (chargeableWeight < 20) return 35;
else if (chargeableWeight < 50) return 30;
else if (chargeableWeight < 100) return 25;
else return 15;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
}
// 加急货物
class ExpediteCargo implements Cargo {
private String id;
private String name;
private double length;
private double width;
private double height;
private double weight;
public ExpediteCargo(String id, String name, double length, double width, double height, double weight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
@Override
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
@Override
public double calculateBaseFee() {
double chargeableWeight = calculateChargeableWeight();
if (chargeableWeight < 20) return 60;
else if (chargeableWeight < 50) return 50;
else if (chargeableWeight < 100) return 40;
else return 30;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
}
// 危险货物
class DangerousCargo implements Cargo {
private String id;
private String name;
private double length;
private double width;
private double height;
private double weight;
public DangerousCargo(String id, String name, double length, double width, double height, double weight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
@Override
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
@Override
public double calculateBaseFee() {
double chargeableWeight = calculateChargeableWeight();
if (chargeableWeight < 20) return 80;
else if (chargeableWeight < 50) return 50;
else if (chargeableWeight < 100) return 30;
else return 20;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
}
// 客户类
class Client {
private String clientType;
private String clientId;
private String name;
private String phone;
private String address;
public Client(String clientType, String clientId, String name, String phone, String address) {
this.clientType = clientType;
this.clientId = clientId;
this.name = name;
this.phone = phone;
this.address = address;
}
public double getDiscountRate() {
return clientType.equals("Corporate") ? 0.8 : 0.9;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
// 航班类
class Flight {
private String flightNumber;
private String departureAirport;
private String arrivalAirport;
private String date;
private double maxLoadCapacity;
private double currentLoad;
public Flight(String flightNumber, String departureAirport, String arrivalAirport, String date, double maxLoadCapacity) {
this.flightNumber = flightNumber;
this.departureAirport = departureAirport;
this.arrivalAirport = arrivalAirport;
this.date = date;
this.maxLoadCapacity = maxLoadCapacity;
this.currentLoad = 0;
}
public boolean canCarry(double weight) {
return currentLoad + weight <= maxLoadCapacity;
}
public void addLoad(double weight) {
currentLoad += weight;
}
public String getFlightNumber() {
return flightNumber;
}
}
// 订单类
class Order {
private String orderId;
private String date;
private String senderName;
private String senderPhone;
private String senderAddress;
private String receiverName;
private String receiverPhone;
private String receiverAddress;
private Client client;
private Flight flight;
private Payment payment;
private List<Cargo> cargos;
public Order(String orderId, String date, String senderName, String senderPhone, String senderAddress,
String receiverName, String receiverPhone, String receiverAddress, Client client, Flight flight,
Payment payment, List<Cargo> cargos) {
this.orderId = orderId;
this.date = date;
this.senderName = senderName;
this.senderPhone = senderPhone;
this.senderAddress = senderAddress;
this.receiverName = receiverName;
this.receiverPhone = receiverPhone;
this.receiverAddress = receiverAddress;
this.client = client;
this.flight = flight;
this.payment = payment;
this.cargos = cargos;
}
public double calculateTotalWeight() {
return cargos.stream().mapToDouble(Cargo::getWeight).sum();
}
public double calculateTotalFee() {
double total = cargos.stream()
.mapToDouble(c -> c.calculateChargeableWeight() * c.calculateBaseFee() * client.getDiscountRate())
.sum();
return total;
}
public String generateOrderReport() {
StringBuilder sb = new StringBuilder();
sb.append("客户:").append(client.getName()).append("(").append(client.getPhone()).append(")订单信息如下:\n");
sb.append("-----------------------------------------\n");
sb.append("航班号:").append(flight.getFlightNumber()).append("\n");
sb.append("订单号:").append(orderId).append("\n");
sb.append("订单日期:").append(date).append("\n");
sb.append("发件人姓名:").append(senderName).append("\n");
sb.append("发件人电话:").append(senderPhone).append("\n");
sb.append("发件人地址:").append(senderAddress).append("\n");
sb.append("收件人姓名:").append(receiverName).append("\n");
sb.append("收件人电话:").append(receiverPhone).append("\n");
sb.append("收件人地址:").append(receiverAddress).append("\n");
sb.append("订单总重量(kg):").append(String.format("%.1f", calculateTotalWeight())).append("\n");
sb.append(payment.pay(calculateTotalFee())).append("\n");
return sb.toString();
}
public String generateCargoDetails() {
StringBuilder sb = new StringBuilder();
sb.append("货物明细如下:\n");
sb.append("-----------------------------------------\n");
sb.append("明细编号 货物名称 计费重量 计费费率 应交运费\n");
int index = 1;
for (Cargo cargo : cargos) {
double chargeableWeight = cargo.calculateChargeableWeight();
double baseFee = cargo.calculateBaseFee();
double fee = chargeableWeight * baseFee * client.getDiscountRate();
sb.append(index).append(" ")
.append(cargo.getName()).append(" ")
.append(String.format("%.1f", chargeableWeight)).append(" ")
.append(String.format("%.1f", baseFee)).append(" ")
.append(String.format("%.1f", fee)).append("\n");
index++;
}
return sb.toString();
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取客户信息
String clientType = scanner.nextLine();
String clientId = scanner.nextLine();
String clientName = scanner.nextLine();
String clientPhone = scanner.nextLine();
String clientAddress = scanner.nextLine();
Client client = new Client(clientType, clientId, clientName, clientPhone, clientAddress);
// 读取货物信息
String cargoType = scanner.nextLine();
int cargoCount = Integer.parseInt(scanner.nextLine());
List<Cargo> cargos = new ArrayList<>();
for (int i = 0; i < cargoCount; i++) {
String cargoId = scanner.nextLine();
String cargoName = 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());
Cargo cargo;
switch (cargoType) {
case "Normal":
cargo = new NormalCargo(cargoId, cargoName, length, width, height, weight);
break;
case "Expedite":
cargo = new ExpediteCargo(cargoId, cargoName, length, width, height, weight);
break;
case "Dangerous":
cargo = new DangerousCargo(cargoId, cargoName, length, width, height, weight);
break;
default:
throw new IllegalArgumentException("Invalid cargo type");
}
cargos.add(cargo);
}
// 读取航班信息
String flightNumber = scanner.nextLine();
String departureAirport = scanner.nextLine();
String arrivalAirport = scanner.nextLine();
String flightDate = scanner.nextLine();
double maxLoadCapacity = Double.parseDouble(scanner.nextLine());
Flight flight = new Flight(flightNumber, departureAirport, arrivalAirport, flightDate, maxLoadCapacity);
// 检查航班载重
double totalWeight = cargos.stream().mapToDouble(Cargo::getWeight).sum();
if (!flight.canCarry(totalWeight)) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.", flightNumber);
return;
}
// 读取订单信息
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();
String paymentMethod = scanner.nextLine();
Payment payment;
switch (paymentMethod) {
case "Wechat":
payment = new WeChatPayment();
break;
case "ALiPay":
payment = new AliPayPayment();
break;
case "Cash":
payment = new CashPayment();
break;
default:
throw new IllegalArgumentException("Invalid payment method");
}
// 创建订单
Order order = new Order(orderId, orderDate, senderName, senderPhone, senderAddress,
receiverName, receiverPhone, receiverAddress, client, flight, payment, cargos);
// 输出结果
System.out.print(order.generateOrderReport());
System.out.print(order.generateCargoDetails());
}
}
点击查看代码
import java.util.*;
interface Payment {
String pay(double amount);
}
class WeChatPayment implements Payment {
@Override
public String pay(double amount) {
return String.format("微信支付金额:%.1f", amount);
}
}
class AliPayPayment implements Payment {
@Override
public String pay(double amount) {
return String.format("支付宝支付金额:%.1f", amount);
}
}
class CashPayment implements Payment {
@Override
public String pay(double amount) {
return String.format("现金支付金额:%.1f", amount);
}
}
interface Cargo {
double calculateChargeableWeight();
double calculateBaseFee();
String getName();
double getWeight();
}
class NormalCargo implements Cargo {
private String id;
private String name;
private double length;
private double width;
private double height;
private double weight;
public NormalCargo(String id, String name, double length, double width, double height, double weight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
@Override
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
@Override
public double calculateBaseFee() {
double chargeableWeight = calculateChargeableWeight();
if (chargeableWeight < 20) return 35;
else if (chargeableWeight < 50) return 30;
else if (chargeableWeight < 100) return 25;
else return 15;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
}
class ExpediteCargo implements Cargo {
private String id;
private String name;
private double length;
private double width;
private double height;
private double weight;
public ExpediteCargo(String id, String name, double length, double width, double height, double weight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
@Override
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
@Override
public double calculateBaseFee() {
double chargeableWeight = calculateChargeableWeight();
if (chargeableWeight < 20) return 60;
else if (chargeableWeight < 50) return 50;
else if (chargeableWeight < 100) return 40;
else return 30;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
}
class DangerousCargo implements Cargo {
private String id;
private String name;
private double length;
private double width;
private double height;
private double weight;
public DangerousCargo(String id, String name, double length, double width, double height, double weight) {
this.id = id;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
@Override
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
@Override
public double calculateBaseFee() {
double chargeableWeight = calculateChargeableWeight();
if (chargeableWeight < 20) return 80;
else if (chargeableWeight < 50) return 50;
else if (chargeableWeight < 100) return 30;
else return 20;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
}
class Client {
private String clientType;
private String clientId;
private String name;
private String phone;
private String address;
public Client(String clientType, String clientId, String name, String phone, String address) {
this.clientType = clientType;
this.clientId = clientId;
this.name = name;
this.phone = phone;
this.address = address;
}
public double getDiscountRate() {
return clientType.equals("Corporate") ? 0.8 : 0.9;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
class Flight {
private String flightNumber;
private String departureAirport;
private String arrivalAirport;
private String date;
private double maxLoadCapacity;
private double currentLoad;
public Flight(String flightNumber, String departureAirport, String arrivalAirport, String date, double maxLoadCapacity) {
this.flightNumber = flightNumber;
this.departureAirport = departureAirport;
this.arrivalAirport = arrivalAirport;
this.date = date;
this.maxLoadCapacity = maxLoadCapacity;
this.currentLoad = 0;
}
public boolean canCarry(double weight) {
return currentLoad + weight <= maxLoadCapacity;
}
public void addLoad(double weight) {
currentLoad += weight;
}
public String getFlightNumber() {
return flightNumber;
}
}
class Order {
private String orderId;
private String date;
private String senderName;
private String senderPhone;
private String senderAddress;
private String receiverName;
private String receiverPhone;
private String receiverAddress;
private Client client;
private Flight flight;
private Payment payment;
private List<Cargo> cargos;
public Order(String orderId, String date, String senderName, String senderPhone, String senderAddress,
String receiverName, String receiverPhone, String receiverAddress, Client client, Flight flight,
Payment payment, List<Cargo> cargos) {
this.orderId = orderId;
this.date = date;
this.senderName = senderName;
this.senderPhone = senderPhone;
this.senderAddress = senderAddress;
this.receiverName = receiverName;
this.receiverPhone = receiverPhone;
this.receiverAddress = receiverAddress;
this.client = client;
this.flight = flight;
this.payment = payment;
this.cargos = cargos;
}
public double calculateTotalWeight() {
return cargos.stream().mapToDouble(Cargo::getWeight).sum();
}
public double calculateTotalFee() {
double total = cargos.stream()
.mapToDouble(c -> c.calculateChargeableWeight() * c.calculateBaseFee())
.sum();
return total * client.getDiscountRate();
}
public String generateOrderReport() {
StringBuilder sb = new StringBuilder();
sb.append("客户:").append(client.getName()).append("(").append(client.getPhone()).append(")订单信息如下:\n");
sb.append("-----------------------------------------\n");
sb.append("航班号:").append(flight.getFlightNumber()).append("\n");
sb.append("订单号:").append(orderId).append("\n");
sb.append("订单日期:").append(date).append("\n");
sb.append("发件人姓名:").append(senderName).append("\n");
sb.append("发件人电话:").append(senderPhone).append("\n");
sb.append("发件人地址:").append(senderAddress).append("\n");
sb.append("收件人姓名:").append(receiverName).append("\n");
sb.append("收件人电话:").append(receiverPhone).append("\n");
sb.append("收件人地址:").append(receiverAddress).append("\n");
sb.append("订单总重量(kg):").append(String.format("%.1f", calculateTotalWeight())).append("\n");
sb.append(payment.pay(calculateTotalFee())).append("\n\n");
return sb.toString();
}
public String generateCargoDetails() {
StringBuilder sb = new StringBuilder();
sb.append("货物明细如下:\n");
sb.append("-----------------------------------------\n");
sb.append("明细编号\t货物名称\t计费重量\t计费费率\t应交运费\n");
int index = 1;
for (Cargo cargo : cargos) {
double chargeableWeight = cargo.calculateChargeableWeight();
double baseFee = cargo.calculateBaseFee();
double fee = chargeableWeight * baseFee;
sb.append(index).append("\t")
.append(cargo.getName()).append("\t")
.append(String.format("%.1f", chargeableWeight)).append("\t")
.append(String.format("%.1f", baseFee)).append("\t")
.append(String.format("%.1f", fee)).append("\n");
index++;
}
return sb.toString();
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String clientType = scanner.nextLine();
String clientId = scanner.nextLine();
String clientName = scanner.nextLine();
String clientPhone = scanner.nextLine();
String clientAddress = scanner.nextLine();
Client client = new Client(clientType, clientId, clientName, clientPhone, clientAddress);
String cargoType = scanner.nextLine();
int cargoCount = Integer.parseInt(scanner.nextLine());
List<Cargo> cargos = new ArrayList<>();
for (int i = 0; i < cargoCount; i++) {
String cargoId = scanner.nextLine();
String cargoName = 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());
Cargo cargo;
switch (cargoType) {
case "Normal":
cargo = new NormalCargo(cargoId, cargoName, length, width, height, weight);
break;
case "Expedite":
cargo = new ExpediteCargo(cargoId, cargoName, length, width, height, weight);
break;
case "Dangerous":
cargo = new DangerousCargo(cargoId, cargoName, length, width, height, weight);
break;
default:
throw new IllegalArgumentException("Invalid cargo type");
}
cargos.add(cargo);
}
String flightNumber = scanner.nextLine();
String departureAirport = scanner.nextLine();
String arrivalAirport = scanner.nextLine();
String flightDate = scanner.nextLine();
double maxLoadCapacity = Double.parseDouble(scanner.nextLine());
Flight flight = new Flight(flightNumber, departureAirport, arrivalAirport, flightDate, maxLoadCapacity);
double totalWeight = cargos.stream().mapToDouble(Cargo::getWeight).sum();
if (!flight.canCarry(totalWeight)) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.", flightNumber);
return;
}
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();
String paymentMethod = scanner.nextLine();
Payment payment;
switch (paymentMethod) {
case "Wechat":
payment = new WeChatPayment();
break;
case "ALiPay":
payment = new AliPayPayment();
break;
case "Cash":
payment = new CashPayment();
break;
default:
throw new IllegalArgumentException("Invalid payment method");
}
// 创建订单
Order order = new Order(orderId, orderDate, senderName, senderPhone, senderAddress,
receiverName, receiverPhone, receiverAddress, client, flight, payment, cargos);
// 输出结果
System.out.print(order.generateOrderReport());
System.out.print(order.generateCargoDetails());
}
}

一、数据类型与输入处理
魔方问题:输入时需接收多种数据类型,像魔方颜色(字符串)、阶数(整数)、边长(浮点数) 。起初未考虑输入错误情况,比如输入非数字字符替代数值,程序直接报错崩溃。后来在代码中添加try - catch块捕获异常,若输入格式不符,友好提示错误,增强了程序健壮性。
点线问题:同样存在输入多种类型数据的情况。在读取点的横纵坐标(浮点数)、颜色(字符串)等时,未做格式校验,导致输入混乱时程序异常。改进方法是先验证输入数据格式是否正确,再进行后续操作,确保数据可靠性。
二、继承与多态的实现
魔方问题:在设计类层次结构实现多态时,对抽象类Solid和RubikCube的抽象方法定义和子类重写理解不深。子类重写方法时,方法签名未严格与抽象方法一致,导致多态调用失败。仔细核对方法名、参数列表和返回值类型后,才正确实现多态,让不同魔方类型能按预期计算和展示面积、体积。
点线问题:将Point、Line、Plane类继承自抽象类Element来实现多态,起初在Element类引用指向子类对象调用display()方法时,出现错误。经排查发现是对父类引用调用子类重写方法的机制掌握不牢,未确保子类重写方法符合多态规则,调整后成功实现不同几何对象的多态展示。
三、几何计算与逻辑处理
魔方问题:计算魔方表面积和体积时,因魔方边长由阶数和单元边长决定,公式推导和代码实现易出错。如正方体魔方表面积计算,未正确考虑阶数与单元边长关系,导致结果错误。反复核对几何公式,理清逻辑后才得到准确结果。
点线问题:Line类中计算线段长度,使用两点间距离公式,在处理坐标差值计算和平方根运算时,数据精度和运算顺序需特别注意。开始因未处理好精度问题,导致长度计算结果有偏差,通过合理使用格式化输出和检查运算逻辑解决了该问题。、
- 计费重量计算的理解偏差
坑点描述:最初没有完全理解题目中"以实际重量和体积重量中的较高者作为计费重量"这一规则,直接使用了实际重量进行计算。
问题表现:
对于体积较大的货物,运费计算明显偏低
与预期结果不符,特别是轻抛货(体积大但重量轻)的情况
解决方案:
java
// 修正后的计费重量计算方法
public double calculateChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight); // 取实际重量和体积重量的较大值
}
心得:
必须仔细阅读题目需求,特别是业务规则部分
对专业术语(如"体积重量")要准确理解
可以先用纸笔计算几个测试用例验证理解是否正确
- 折扣应用不一致问题
坑点描述:在订单总运费和货物明细运费计算中,折扣应用方式不一致,导致最终金额对不上。
问题表现:
订单总金额显示的是折扣后金额
货物明细中显示的是折扣前金额
用户对账时会产生困惑
解决方案:
java
// 统一应用折扣的计算方式
public double calculateItemFee(Cargo cargo) {
double baseFee = cargo.calculateChargeableWeight() * cargo.calculateBaseFee();
return baseFee * client.getDiscountRate(); // 统一应用折扣
}
心得:
业务规则应在全系统保持一致
金额计算这类核心逻辑最好集中处理
在需求分析阶段就应明确折扣的应用场景
- 面向对象设计原则的应用
坑点描述:初期没有很好遵循SOLID原则,导致代码难以扩展和维护。
具体问题:
将不同货物类型的费率计算硬编码在一个类中
支付方式直接使用条件判断实现
客户折扣逻辑与订单耦合过紧
改进方案:
java
// 使用策略模式处理不同货物类型
public interface Cargo {
double calculateBaseFee();
}
// 不同货物类型实现各自费率计算
public class ExpediteCargo implements Cargo {
public double calculateBaseFee() {
// 加急货物特定费率计算
}
}
心得:
开闭原则(OCP)确实能提高系统可扩展性
依赖倒置(DIP)使高层模块不依赖低层细节
单一职责(SRP)让每个类更易理解和维护
设计模式不是银弹,但要学会识别适用场景
4. 输入输出格式处理
坑点描述:没有严格按照题目要求的格式处理输入输出,导致PTA平台判题失败。
常见问题:
输入顺序错误,读取数据错位
输出格式中空格、换行不符合要求
数字精度不统一(有时保留1位小数,有时不保留)
解决方案:
点击查看代码
// 严格遵循输出格式要求
System.out.printf("明细编号\t货物名称\t计费重量\t计费费率\t应交运费%n");
System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f%n",
index,
cargo.getName(),
chargeableWeight,
baseFee,
fee);
点击查看代码
// 在创建订单前先校验载重
double totalWeight = calculateTotalWeight(cargos);
if (!flight.canCarry(totalWeight)) {
System.out.printf("航班%s已超载%n", flight.getNumber());
return;
}
// 校验通过才创建订单
Order order = new Order(...);
点击查看代码
// 添加边界测试用例
@Test
public void testChargeableWeightBoundary() {
// 体积重量≈实际重量
Cargo cargo = new NormalCargo(..., 60, 50, 40, 20); // 体积重量=20kg
assertEquals(20, cargo.calculateChargeableWeight(), 0.01);
// 实际重量略大于体积重量
cargo = new NormalCargo(..., 60, 50, 40, 20.1);
assertEquals(20.1, cargo.calculateChargeableWeight(), 0.01);
}
表现层:处理输入输出,格式转换
业务逻辑层:核心运费计算、订单处理
数据访问层:航班、订单等数据的持久化
- 引入依赖注入
java
// 使用工厂模式创建支付处理器
public class PaymentFactory {
public static Payment create(String type) {
switch(type) {
case "Wechat": return new WeChatPayment();
case "ALiPay": return new AliPayPayment();
case "Cash": return new CashPayment();
default: throw new IllegalArgumentException();
}
}
}
// 在订单服务中使用
Payment payment = PaymentFactory.create(paymentType);
功能扩展建议
- 多级费率体系增强
java
// 支持季节性和区域性费率调整
public interface RateStrategy {
double adjustBaseRate(double originalRate, LocalDate date, String route);
}
// 实现类示例
public class HolidayRateStrategy implements RateStrategy {
public double adjustBaseRate(double originalRate, LocalDate date, String route) {
return isHoliday(date) ? originalRate * 1.2 : originalRate;
}
}
2. 货物类型扩展机制
java
// 通过配置文件动态加载货物类型
@Configuration
@PropertySource("classpath:cargo-types.properties")
public class CargoConfig {
@Value("#{${cargo.types}}")
private Map<String, String> cargoTypes;
// 注册所有货物类型处理器
@Bean
public Map<String, CargoHandler> cargoHandlers() {
// 实现动态加载
}
}
三、用户体验改进
-
交互式命令行界面
java
// 改进后的用户交互流程
public void startInteractiveMode() {
Scanner sc = new Scanner(System.in);
System.out.println("请选择操作:");
System.out.println("1. 创建新订单");
System.out.println("2. 查询订单状态");
System.out.println("3. 航班信息查询");int choice = sc.nextInt();
switch(choice) {
case 1: createOrderInteractive(sc); break;
// 其他选项处理
}
} -
结果可视化输出
java
// 生成ASCII表格输出
public void printCargoTable(Listcargos) {
System.out.println("+----+------------+---------+---------+---------+");
System.out.println("| ID | 货物名称 | 重量 | 费率 | 运费 |");
System.out.println("+----+------------+---------+---------+---------+");cargos.forEach(c -> {
System.out.printf("| %2d | %-10s | %7.1f | %7.1f | %7.1f |%n",
c.getId(), c.getName(),
c.getWeight(), c.getRate(), c.getFee());
});System.out.println("+----+------------+---------+---------+---------+");
}
四、性能优化建议 -
航班载重预计算
java
// 使用缓存预计算航班剩余容量
public class FlightCapacityCache {
private Map<String, Double> capacityMap = new ConcurrentHashMap<>();public boolean checkCapacity(String flightNo, double weight) {
return capacityMap.compute(flightNo, (k, v) -> {
double remaining = (v == null) ? getFromDB(flightNo) : v;
return remaining - weight;
}) >= 0;
}
} -
批量处理优化
java
// 使用并行流处理大批量货物计算
public double calculateTotalFee(Listcargos) {
return cargos.parallelStream()
.mapToDouble(c -> c.calculateChargeableWeight()
* c.getRate()
* getDiscount())
.sum();
}
五、异常处理增强 -
自定义异常体系
java
// 定义业务异常层次
public abstract class ShippingException extends RuntimeException {
public ShippingException(String message) {
super(message);
}
}
public class OverweightException extends ShippingException {
public OverweightException(double max) {
super("超过最大载重限制: " + max + "kg");
}
}
2. 异常处理建议
java
// 统一的异常处理机制
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(OverweightException.class)
public ResponseEntity
return ResponseEntity.badRequest()
.body("错误: " + ex.getMessage());
}
@ExceptionHandler(InputException.class)
public ResponseEntity
return ResponseEntity.badRequest()
.body("输入错误: " + ex.getMessage());
}
}
六、测试覆盖率提升
-
参数化测试示例
java
@RunWith(Parameterized.class)
public class FeeCalculationTest {@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ "Normal", 15, 35 }, // 普通货物15kg
{ "Normal", 25, 30 }, // 普通货物25kg
{ "Dangerous", 10, 80 } // 危险品10kg
});
}@Test
public void testRateCalculation() {
// 测试逻辑
}
} -
边界测试建议
java
// 临界值测试用例设计
@Test
public void testWeightBoundaries() {
// 费率分界点测试
testRate(19.9, 35); // 普通货物<20kg
testRate(20.0, 30); // 普通货物≥20kg
testRate(49.9, 30); // 普通货物<50kg
testRate(50.0, 25); // 普通货物≥50kg
}
七、部署与维护建议
配置化管理:
将费率表、折扣规则等移至外部配置文件
支持热更新不重启应用
监控指标:
java
// 使用Micrometer添加监控
@Bean
public MeterRegistryCustomizer
return registry -> {
registry.gauge("flight.capacity.usage", flightService,
s -> s.getCapacityUsageRatio());
};
}
文档自动化:
使用Swagger生成API文档
维护CHANGELOG记录版本变更
总结
这些改进建议从多个维度提升了系统的:
可维护性:通过分层设计和清晰架构
扩展性:支持新货物类型和费率策略
健壮性:完善的异常处理和边界检查
用户体验:更友好的交互界面
性能:批量处理和缓存优化
- 核心技术掌握
面向对象设计原则:深入理解了SOLID原则在实际项目中的应用价值,特别是:
单一职责原则使代码更易维护
开闭原则提高了系统扩展性
依赖倒置降低了模块耦合度
设计模式实践:成功应用了策略模式(货物类型处理)、工厂模式(支付方式创建)等常用模式
异常处理体系:构建了层次化的业务异常处理机制
- 工程能力提升
测试驱动开发:认识到先写测试用例对需求澄清的重要性
持续重构:体验了迭代式改进对代码质量的提升效果
API设计:学习了如何设计易用且可扩展的接口
- 业务理解深化
掌握了航空货运的核心业务规则:
计费重量计算逻辑
多维度费率体系
航班载重管理机制


浙公网安备 33010602011771号