
本文详细介绍了如何在Java中处理异构用户输入数据,利用多态性将不同类型的对象(如`Plant`和`Flower`)存储在同一个`ArrayList`中。通过解析用户输入的字符串,动态创建对象并将其添加到集合,最后使用统一的方法遍历并打印集合中所有对象的详细信息,从而展示了面向对象设计与集合框架的强大结合。
引言:Java中异构数据处理与多态集合
在Java应用程序开发中,我们经常需要从用户那里获取不同格式的数据,并将其存储为相应的对象。当这些对象具有共同的基类但又各自拥有独特的属性时,如何有效地解析、存储和管理它们就成为了一个关键问题。本教程将以一个具体的植物园管理示例,深入探讨如何利用Java的面向对象特性(特别是多态性)和集合框架(ArrayList),来优雅地解决这类异构数据处理的挑战。我们将学习如何读取多行用户输入,根据输入类型动态创建对象,并将这些不同类型的对象统一存储在一个ArrayList中,最终实现对所有对象的统一遍历和信息展示。
核心概念解析
为了实现上述目标,我们需要理解并应用以下几个核心Java概念:
1. 多态性与ArrayList的应用
Java中的多态性允许我们使用父类类型的引用来指向子类对象。这意味着,一个声明为ArrayList
立即学习“Java免费学习笔记(深入)”;
2. 用户输入解析策略
从用户处读取多行、多字段的字符串输入是常见需求。
- Scanner.nextLine(): 相较于Scanner.next(),nextLine()方法能够读取当前行中剩余的所有字符,直到遇到行终止符(如回车键)。这对于处理包含多个单词的完整一行输入至关重要。
- String.split(): String类的split()方法可以根据指定的分隔符将一个字符串分割成一个字符串数组。例如,使用空格作为分隔符可以将一行输入分解为各个独立的字段,方便后续的数据提取和类型转换。
3. 数据类型转换
从字符串中解析出的数据往往需要转换为特定的数据类型,以便赋值给对象的属性。
- Integer.parseInt() / Double.parseDouble(): 当字符串表示数字时,需要使用这些方法将其转换为int或double类型。
- Boolean.parseBoolean(): 当字符串表示布尔值("true"或"false")时,可以使用此方法将其转换为boolean类型。
实现步骤详解
我们将通过一个具体的PlantArrayListExample程序来演示上述概念的实现。
1. 定义基类与派生类
首先,我们需要定义Plant基类和Flower派生类。Plant类包含植物名称和成本,Flower类继承Plant并额外包含是否为一年生植物和花的颜色。两个类都将实现一个printInfo()方法来打印各自的详细信息。
Plant.java
ECTouch是上海商创网络科技有限公司推出的一套基于 PHP 和 MySQL 数据库构建的开源且易于使用的移动商城网店系统!应用于各种服务器平台的高效、快速和易于管理的网店解决方案,采用稳定的MVC框架开发,完美对接ecshop系统与模板堂众多模板,为中小企业提供最佳的移动电商解决方案。ECTouch程序源代码完全无加密。安装时只需将已集成的文件夹放进指定位置,通过浏览器访问一键安装,无需对已有
public class Plant {
private String plantName;
private int plantCost; // 假设成本为整数
public Plant() {
this.plantName = "unknown";
this.plantCost = 0;
}
public void setPlantName(String plantName) {
this.plantName = plantName;
}
public void setPlantCost(int plantCost) {
this.plantCost = plantCost;
}
public String getPlantName() {
return plantName;
}
public int getPlantCost() {
return plantCost;
}
public void printInfo() {
System.out.println("Plant Name: " + plantName + ", Cost: " + plantCost);
}
}Flower.java
public class Flower extends Plant {
private boolean isAnnual;
private String colorOfFlowers;
public Flower() {
super(); // 调用父类构造器
this.isAnnual = false;
this.colorOfFlowers = "none";
}
public void setAnnual(boolean isAnnual) { // 命名更清晰
this.isAnnual = isAnnual;
}
public void setColorOfFlowers(String colorOfFlowers) {
this.colorOfFlowers = colorOfFlowers;
}
public boolean getIsAnnual() {
return isAnnual;
}
public String getColorOfFlowers() {
return colorOfFlowers;
}
@Override
public void printInfo() {
System.out.println("Flower Name: " + getPlantName() + ", Cost: " + getPlantCost() +
", Annual: " + isAnnual + ", Color: " + colorOfFlowers);
}
}2. 初始化与输入循环
在main方法中,我们需要创建一个Scanner对象来读取用户输入,以及一个ArrayList
import java.util.Scanner;
import java.util.ArrayList;
public class PlantArrayListExample {
// ... printArrayList method will be here ...
public static void main(String[] args) {
Scanner scnr = new Scanner(System.in);
ArrayList myGarden = new ArrayList();
String inputLine; // 用于存储每行输入
System.out.println("Enter plant or flower information (e.g., 'plant Spirea 10' or 'flower Hydrangea 30 false lilac'). Enter -1 to finish:");
// 首次读取一行输入
inputLine = scnr.nextLine();
while (!inputLine.equals("-1")) {
// ... 解析和对象创建逻辑 ...
// 读取下一行输入
inputLine = scnr.nextLine();
}
// ... 打印集合内容 ...
scnr.close(); // 关闭Scanner
}
} 3. 解析每行输入
在while循环内部,使用String.split(" ")将当前行输入分割成字符串数组。
String[] info = inputLine.split(" ");
if (info.length == 0) { // 处理空行
inputLine = scnr.nextLine();
continue;
}4. 条件创建对象并赋值
根据info[0](第一个字段,表示类型)的值,判断是创建Plant对象还是Flower对象,并根据后续字段为对象的属性赋值。注意数据类型的转换。
String type = info[0].toLowerCase(); // 获取类型并转为小写
String plantName = info[1];
int plantCost = Integer.parseInt(info[2]); // 转换为int
if (type.equals("plant")) {
Plant p = new Plant();
p.setPlantName(plantName);
p.setPlantCost(plantCost);
myGarden.add(p);
} else if (type.equals("flower")) {
// 确保有足够的字段来解析花的信息
if (info.length >= 5) {
boolean isAnnual = Boolean.parseBoolean(info[3]); // 转换为boolean
String colorOfFlowers = info[4];
Flower f = new Flower();
f.setPlantName(plantName);
f.setPlantCost(plantCost);
f.setAnnual(isAnnual); // 使用更清晰的setter
f.setColorOfFlowers(colorOfFlowers);
myGarden.add(f);
} else {
System.err.println("Error: Incomplete flower information: " + inputLine);
}
} else {
System.err.println("Warning: Unknown plant/flower type: " + inputLine);
}5. 添加到集合
无论是Plant对象还是Flower对象,都统一通过myGarden.add()方法添加到ArrayList
6. 统一打印方法
定义一个静态方法printArrayList,它接收ArrayList
public static void printArrayList(ArrayListmyGarden) { System.out.println("\n--- My Garden Contents ---"); for (Plant plant : myGarden) { // 使用增强for循环更简洁 plant.printInfo(); // 多态调用,根据实际对象类型执行对应的printInfo() } System.out.println("--------------------------"); }
完整代码示例
将上述所有部分组合起来,构成完整的PlantArrayListExample.java程序。
PlantArrayListExample.java
import java.util.Scanner;
import java.util.ArrayList;
public class PlantArrayListExample {
/**
* Prints the information of all Plant (or Flower) objects in the given ArrayList.
* Utilizes polymorphism to call the appropriate printInfo() method for each object.
* @param myGarden The ArrayList of Plant objects to print.
*/
public static void printArrayList(ArrayList myGarden) {
System.out.println("\n--- My Garden Contents ---");
if (myGarden.isEmpty()) {
System.out.println("The garden is empty.");
return;
}
for (Plant plant : myGarden) {
plant.printInfo(); // Polymorphic call
}
System.out.println("--------------------------");
}
public static void main(String[] args) {
Scanner scnr = new Scanner(System.in);
ArrayList myGarden = new ArrayList();
String inputLine;
System.out.println("Welcome to My Garden Manager!");
System.out.println("Enter plant or flower information. Use format:");
System.out.println(" 'plant ' (e.g., plant Spirea 10)");
System.out.println(" 'flower ' (e.g., flower Hydrangea 30 false lilac)");
System.out.println("Enter -1 to finish input.");
System.out.println("--------------------------------------------------");
// 首次读取一行输入
inputLine = scnr.nextLine();
while (!inputLine.equals("-1")) {
// 跳过空行
if (inputLine.trim().isEmpty()) {
inputLine = scnr.nextLine();
continue;
}
String[] info = inputLine.split(" ");
// 检查输入字段数量是否足够
if (info.length < 3) {
System.err.println("Error: Insufficient data for input: " + inputLine);
inputLine = scnr.nextLine();
continue;
}
try {
String type = info[0].toLowerCase();
String plantName = info[1];
int plantCost = Integer.parseInt(info[2]); // 尝试将成本字符串转换为整数
if (type.equals("plant")) {
Plant p = new Plant();
p.setPlantName(plantName);
p.setPlantCost(plantCost);
myGarden.add(p);
} else if (type.equals("flower")) {
// 确保花有足够的字段
if (info.length >= 5) {
boolean isAnnual = Boolean.parseBoolean(info[3]);
String colorOfFlowers = info[4];
Flower f = new Flower();
f.setPlantName(plantName);
f.setPlantCost(plantCost);
f.setAnnual(isAnnual);
f.setColorOfFlowers(colorOfFlowers);
myGarden.add(f);
} else {
System.err.println("Error: Incomplete flower information, expected 5 fields: " + inputLine);
}
} else {
System.err.println("Warning: Unknown plant/flower type encountered: " + inputLine);
}
} catch (NumberFormatException e) {
System.err.println("Error: Invalid cost format. Please enter a number for cost. Input: " + inputLine);
} catch (Exception e) { // 捕获其他潜在异常
System.err.println("An unexpected error occurred while processing input: " + inputLine + " - " + e.getMessage());
}
// 读取下一行输入
inputLine = scnr.nextLine();
}
// 调用方法打印myGarden中的所有元素
printArrayList(myGarden);
scnr.close(); // 关闭Scanner,释放资源
System.out.println("Program finished. Goodbye!");
}
} 注意事项与最佳实践
- 输入读取方式: 始终使用scnr.nextLine()来读取完整的行输入,然后对该行进行解析。避免在循环中混用scnr.next()和scnr.nextLine(),因为scnr.next()不会消费行终止符,可能导致scnr.nextLine()读取到空字符串。
- 错误处理: 在进行数据类型转换(如Integer.parseInt())时,务必使用try-catch块来捕获NumberFormatException,以应对用户输入非数字字符的情况,增强程序的健壮性。
- 输入验证: 在解析info数组时,应检查info.length以确保数组索引不会越界。例如,如果用户只输入flower Rose 6,而程序期望5个字段,则需要进行检查。
- 资源管理: 及时关闭Scanner对象(scnr.close()),以释放系统资源,避免资源泄漏。通常在main方法的末尾或finally块中执行。
- 代码可读性: 使用有意义的变量名,并添加注释,提高代码的可读性和可维护性。
- 类的设计: 确保基类和派生类的方法设计合理,例如printInfo()方法在基类中定义,并在派生类中重写,这是多态性的典型应用。
总结
本教程通过一个实际的Java植物园管理系统示例,全面展示了如何结合Java的多态性、ArrayList集合以及字符串解析技术来有效地处理异构用户输入数据。我们学习了如何根据输入类型动态创建不同的对象,并将它们统一存储在泛型集合中,最后通过多态调用实现统一的打印逻辑。掌握这些技术对于开发灵活、可扩展的Java应用程序至关重要。通过遵循上述实现步骤和最佳实践,开发者可以构建出更加健壮和用户友好的应用程序。









