
1. 问题描述
在面向对象编程中,我们经常需要处理包含多种对象类型的数组。一个常见需求是从这些数组中找出满足特定条件(例如,最昂贵或最便宜)的特定类型对象。例如,在一个 FlyingObjects 数组中,我们可能需要找出其中所有 UAV(无人机)对象里价格最高和最低的实例。
初始尝试时,开发者可能会遇到一个常见问题:比较逻辑错误地将对象的价格与数组索引混淆,或者初始化比较变量的方式不当,导致结果总是返回数组中的第一个或最后一个匹配项,而非真正的最大或最小值。
2. 错误的实现逻辑分析
考虑以下不正确的代码片段,它试图找出最昂贵和最便宜的 UAV:
public static void findLeastAndMostExpensiveUAV(FlyingObjects[] flyingObjects) {
int mostExpensive = -1; // 试图用一个int变量同时表示索引和价格(不合理)
int leastExpensive = 1000000000; // 试图用一个int变量同时表示索引和价格(不合理)
boolean hasUav = false;
if(flyingObjects == null) {
System.out.println("There is no UAV");
return; // 添加return防止后续NullPointerException
}
for(int i = 0; i < flyingObjects.length; i++) {
if (flyingObjects[i] instanceof Uav) {
Uav a = (Uav) flyingObjects[i];
// 错误:将对象的价格 (double) 与用于存储索引的int变量进行比较
if (a.getPrice() >= mostExpensive) { // mostExpensive此时是索引值,不是价格
mostExpensive = i; // 错误:将索引赋值给mostExpensive,但它之前用于价格比较
}
if (a.getPrice() <= leastExpensive){ // leastExpensive此时是索引值,不是价格
leastExpensive = i; // 错误:将索引赋值给leastExpensive,但它之前用于价格比较
}
if(!hasUav) {
hasUav = true;
}
}
}
if(!hasUav) {
System.out.println("There is no UAV");
} else {
// 在这里,mostExpensive和leastExpensive实际上存储的是索引,但它们在循环中被当作价格来比较
System.out.println("\nInformation about the most expensive UAV: \n" + flyingObjects[mostExpensive] + "\n");
System.out.println("Information about the least expensive UAV: \n" + flyingObjects[leastExpensive]);
}
}上述代码的主要问题在于:
立即学习“Java免费学习笔记(深入)”;
- 变量混淆: mostExpensive 和 leastExpensive 变量被尝试同时用于存储价格(在比较时)和索引(在赋值时)。它们的初始值 -1 和 1000000000 试图作为价格的边界,但随后又被赋予了数组索引 i。
- 类型不匹配的比较: a.getPrice() 返回 double 类型,而 mostExpensive 和 leastExpensive 是 int 类型。虽然Java会自动进行类型转换,但逻辑上将一个价格与一个索引进行比较是错误的。
- 初始化问题: 即使变量类型正确,mostExpensive 初始化为 0d 或 Double.MIN_VALUE,leastExpensive 初始化为 Double.MAX_VALUE 才是正确的起始点。
3. 正确的实现方法
为了正确地找到最昂贵和最便宜的 UAV,我们需要分离价格和索引的跟踪变量,并确保它们被正确初始化和更新。
3.1 核心思路
- 独立变量: 使用单独的变量来存储当前找到的最高/最低价格,以及这些价格对应的对象在数组中的索引。
-
正确初始化:
- 最高价格初始化为一个足够小的值(例如 0.0 或 Double.MIN_VALUE)。
- 最低价格初始化为一个足够大的值(例如 Double.MAX_VALUE)。
- 索引变量可以初始化为 -1,表示尚未找到任何匹配对象。
- 遍历与类型检查: 遍历数组,使用 instanceof 运算符检查当前对象是否是 UAV 类型。
- 价格与索引更新: 如果当前对象是 UAV,获取其价格,并与当前最高/最低价格进行比较。如果发现新的最高/最低价格,则同时更新价格变量和对应的索引变量。
- 处理无匹配项: 在循环结束后,检查是否找到了任何 UAV 对象(例如,通过检查索引变量是否仍为初始值 -1 或使用一个布尔标志)。
3.2 示例代码
import java.util.Objects; // For Objects.toString() if needed for better output
// 假设 FlyingObjects, Uav, AgriculturalDrone, Mav, Multirotor, Helicopter, Airplane 类已定义
// 且 Uav 类具有 getPrice() 方法
class FlyingObjects {
protected double price;
public FlyingObjects(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "FlyingObject [price=" + price + "]";
}
}
class Uav extends FlyingObjects {
private int someOtherProp; // Example property
public Uav(double price, int someOtherProp) {
super(price);
this.someOtherProp = someOtherProp;
}
@Override
public String toString() {
return "UAV [price=" + price + ", someOtherProp=" + someOtherProp + "]";
}
}
class AgriculturalDrone extends Uav {
private String model;
private double payloadCapacity;
public AgriculturalDrone(double price, int someOtherProp, String model, double payloadCapacity) {
super(price, someOtherProp);
this.model = model;
this.payloadCapacity = payloadCapacity;
}
@Override
public String toString() {
return "AgriculturalDrone [price=" + price + ", model=" + model + ", payloadCapacity=" + payloadCapacity + "]";
}
}
class Mav extends Uav {
private String name;
private int range;
public Mav(double price, int someOtherProp, String name, int range) {
super(price, someOtherProp);
this.name = name;
this.range = range;
}
@Override
public String toString() {
return "MAV [price=" + price + ", name=" + name + ", range=" + range + "]";
}
}
// 示例中未提供的其他类,为完整性假设它们存在并继承自FlyingObjects
class Airplane extends FlyingObjects {
private int maxSpeed;
public Airplane(String model, double price, int maxSpeed) {
super(price);
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Airplane [price=" + price + ", maxSpeed=" + maxSpeed + "]";
}
}
class Helicopter extends Airplane { // Assuming Helicopter extends Airplane based on original tree
private int rotorBlades;
public Helicopter(String model, double price, int maxSpeed, int rotorBlades, int year, int month) {
super(model, price, maxSpeed);
this.rotorBlades = rotorBlades;
}
@Override
public String toString() {
return "Helicopter [price=" + price + ", rotorBlades=" + rotorBlades + "]";
}
}
class Multirotor extends Helicopter {
private int motors;
public Multirotor(String model, double price, int maxSpeed, int rotorBlades, int year, int month, int motors) {
super(model, price, maxSpeed, rotorBlades, year, month);
this.motors = motors;
}
@Override
public String toString() {
return "Multirotor [price=" + price + ", motors=" + motors + "]";
}
}
public class FlyingObjectAnalyzer {
public static void findLeastAndMostExpensiveUAV(FlyingObjects[] flyingObjects) {
// 独立跟踪最高/最低价格和对应的索引
double maxPrice = 0.0; // 初始最高价格可以设为0或Double.MIN_VALUE
int maxPricedUavIndex = -1; // 初始索引设为-1,表示未找到
double minPrice = Double.MAX_VALUE; // 初始最低价格设为Double.MAX_VALUE
int minPricedUavIndex = -1; // 初始索引设为-1,表示未找到
// 检查数组是否为空或null
if (flyingObjects == null || flyingObjects.length == 0) {
System.out.println("The array is empty or null. No UAVs to analyze.");
return;
}
for (int i = 0; i < flyingObjects.length; i++) {
// 检查当前对象是否是UAV或其子类
if (flyingObjects[i] instanceof Uav) {
Uav currentUav = (Uav) flyingObjects[i]; // 安全向下转型
double currentPrice = currentUav.getPrice();
// 更新最高价格和对应的索引
if (currentPrice > maxPrice) {
maxPrice = currentPrice;
maxPricedUavIndex = i;
}
// 更新最低价格和对应的索引
if (currentPrice < minPrice) {
minPrice = currentPrice;
minPricedUavIndex = i;
}
}
}
// 输出结果
if (maxPricedUavIndex != -1) { // 如果找到了UAV
System.out.println("\nInformation about the most expensive UAV: ");
System.out.println(flyingObjects[maxPricedUavIndex]);
System.out.println("Price: " + maxPrice);
System.out.println("\nInformation about the least expensive UAV: ");
System.out.println(flyingObjects[minPricedUavIndex]);
System.out.println("Price: " + minPrice);
} else {
System.out.println("No UAVs found in the array.");
}
}
public static void main(String[] args) {
FlyingObjects[] test = new FlyingObjects[7];
test[0] = new Uav(10, 43);
test[1] = new AgriculturalDrone(8000, 780000, "Chase", 2400);
test[2] = new Uav(10, 5); // Price 10
test[3] = new Mav(0.5, 140000, "trooper", 10); // Price 0.5
test[4] = new Multirotor("Hexa", 140000, 200, 185, 2021, 1, 4); // Not a UAV
test[5] = new Helicopter("Robinson", 199000, 250, 100, 2018, 7); // Not a UAV
test[6] = new Airplane("Boeing", 350000, 450); // Not a UAV
findLeastAndMostExpensiveUAV(test);
System.out.println("\n--- Testing with no UAVs ---");
FlyingObjects[] noUavs = new FlyingObjects[2];
noUavs[0] = new Airplane("Small Plane", 100000, 300);
noUavs[1] = new Helicopter("MiniCopter", 50000, 150, 4, 2020, 5);
findLeastAndMostExpensiveUAV(noUavs);
System.out.println("\n--- Testing with empty array ---");
FlyingObjects[] emptyArray = new FlyingObjects[0];
findLeastAndMostExpensiveUAV(emptyArray);
System.out.println("\n--- Testing with null array ---");
FlyingObjects[] nullArray = null;
findLeastAndMostExpensiveUAV(nullArray);
}
}3.3 注意事项与总结
- 变量语义清晰: 始终确保变量的命名和用途保持一致。maxPrice 存储价格,maxPricedUavIndex 存储索引,避免混淆。
-
正确初始化边界值:
- 查找最小值时,初始值应设置为可能的最大值 (Double.MAX_VALUE),以便任何实际值都能成为第一个最小值。
- 查找最大值时,初始值应设置为可能的最小值 (0.0 或 Double.MIN_VALUE),以便任何实际值都能成为第一个最大值。
- 索引初始化为 -1 是一个好的实践,它表示“未找到”,因为有效的数组索引总是非负的。
- 类型检查与向下转型: 使用 instanceof 运算符进行类型检查是安全的,确保在向下转型 ((Uav) flyingObjects[i]) 之前对象确实是目标类型或其子类,避免 ClassCastException。
- 处理空数组或无匹配项: 在遍历之前检查数组是否为 null 或空,并在循环结束后检查是否实际找到了匹配的对象(通过检查索引是否仍为初始的 -1),这增强了代码的健壮性。
- 继承层级: instanceof 运算符会检查对象是否是指定类的实例,或者指定类的子类的实例。这意味着如果 AgriculturalDrone 和 Mav 继承自 Uav,那么 instanceof Uav 也会对它们返回 true,这是符合预期的行为。
通过遵循这些原则,可以有效地在复杂对象数组中定位和分析特定类型的元素,确保程序的正确性和健壮性。









