在Java编程中,我们经常需要处理包含多个自定义对象的数组或集合。这些对象通常封装了多种类型的数据,例如字符串(String)和整数(int)。本教程将以一个学生成绩管理的示例,详细讲解如何从一个学生对象数组中提取并计算特定数值属性(如学生分数)的平均值和最高分。
1. 理解与完善 Student 类
首先,我们需要一个表示学生信息的类。这个 student 类应包含学生的姓名(string)和分数(int)。为了使数据可访问和修改,还需要提供相应的构造器、getter和setter方法。
原始的 Student 类设计:
import java.util.Scanner; public class Student { private String name; private int score; public Student() { } public Student(String name, int score){ this.name = name; this.score = score; } public void setName(String name) { this.name = name; } public void setScore(int score) { this.score = score; } public void readInput() { Scanner keyboard = new Scanner(System.in); System.out.println("Please enter the student's name: "); this.name = keyboard.next(); System.out.println("Please enter the student's score: "); this.score = keyboard.nextInt(); } public void writeOutput() { System.out.println("The student's name and score: " + name + ", " + score + "%"); } // 原始设计中的Getter方法有误 // public String getName(String name) { // return this.name; // } // public int getScore(int score) { // return score; // } }
问题分析与修正: 原始 Student 类中的 getName(String name) 和 getScore(int score) 方法签名不正确。标准的Getter方法不应接受参数,它们的作用是返回私有成员变量的值。
修正后的 Student 类:
import java.util.Scanner; // 确保导入Scanner public class Student { private String name; private int score; public Student() { // 默认构造器,可用于创建空对象 } public Student(String name, int score){ this.name = name; this.score = score; } // Setter方法 public void setName(String name) { this.name = name; } public void setScore(int score) { this.score = score; } // Getter方法:不应有参数 public String getName() { // 修正:移除参数 return this.name; } public int getScore() { // 修正:移除参数 return this.score; // 修正:返回实例变量this.score } // 从控制台读取学生信息 public void readInput() { Scanner keyboard = new Scanner(System.in); // 每次调用都创建新Scanner可能不是最佳实践,但在此示例中可接受 System.out.println("Please enter the student's name: "); this.name = keyboard.next(); System.out.println("Please enter the student's score: "); this.score = keyboard.nextInt(); // keyboard.close(); // 在方法内部关闭Scanner可能会导致后续输入问题,通常在程序结束时统一关闭 } // 输出学生信息 public void writeOutput() { System.out.println("The student's name and score: " + name + ", " + score + "%"); } }
注意事项: 在实际应用中,Scanner 对象通常在程序的入口点创建一次,并作为参数传递或通过其他方式共享,以避免资源泄露和不必要的对象创建。
2. 实现成绩统计逻辑 (TestReporter 类)
TestReporter 类负责管理学生数组,并计算班级的平均分和最高分。
立即学习“Java免费学习笔记(深入)”;
原始的 TestReporter 类设计:
import java.lang.reflect.Array; import java.util.Arrays; import java.util.Scanner; public class TestReporter { private int highestScore; private double averageScore; private Student[] ourClass; private int numOfStudents; public TestReporter(){ } public void getData() { Scanner keyboard = new Scanner(System.in); System.out.println("Please enter the number of students"); numOfStudents = keyboard.nextInt(); ourClass = new Student[numOfStudents]; for (int i = 0; i < numOfStudents ; i++) { ourClass[i] = new Student(); ourClass[i].readInput(); } // keyboard.close(); // 同Student类,此处关闭Scanner可能导致问题 } public void computeStats() { double total = 0; for (int i = 0; i < numOfStudents; i++) { // 原始代码:total = total + ourClass[i]; // 错误:不能将Student对象直接加到double } // averageScore = total / ourClass.length; // 原始代码:在循环外计算,但total计算有误 } public void displayResults() { for (Student Student: ourClass) { Student.writeOutput(); } } }
问题分析与修正:
- computeStats 方法中的错误:
- total = total + ourClass[i];:ourClass[i] 是一个 Student 对象,不能直接与 double 类型的 total 相加。正确的做法是调用 ourClass[i].getScore() 获取分数。
- 没有计算 highestScore 的逻辑。
- highestScore 和 averageScore 应该更新类的成员变量,而不是在方法内部声明局部变量(除非有特殊需求)。
- displayResults 方法的调用顺序: 在显示结果之前,需要确保 computeStats 已经被调用,以更新 highestScore 和 averageScore。
修正后的 TestReporter 类:
import java.util.Scanner; // 仅需导入Scanner,其他可能不需要 public class TestReporter { private int highestScore; private double averageScore; private Student[] ourClass; private int numOfStudents; public TestReporter(){ // 构造器可以进行一些初始化,例如将最高分初始化为0或Integer.MIN_VALUE this.highestScore = 0; // 假设分数非负 this.averageScore = 0.0; this.numOfStudents = 0; } // 获取学生数据 public void getData() { Scanner keyboard = new Scanner(System.in); System.out.println("Please enter the number of students:"); numOfStudents = keyboard.nextInt(); ourClass = new Student[numOfStudents]; for (int i = 0; i < numOfStudents ; i++) { ourClass[i] = new Student(); System.out.println("n--- Entering data for Student " + (i + 1) + " ---"); ourClass[i].readInput(); } // 考虑到readInput内部也创建Scanner,此处不关闭,由系统或更上层管理 } // 计算统计数据:平均分和最高分 public void computeStats() { if (numOfStudents == 0 || ourClass == null) { System.out.println("No student data available to compute stats."); this.averageScore = 0.0; this.highestScore = 0; return; } double total = 0; this.highestScore = 0; // 初始化最高分为0,适用于非负分数。若分数可能为负,应初始化为Integer.MIN_VALUE。 for (int i = 0; i < numOfStudents; i++) { int currentScore = ourClass[i].getScore(); // 正确获取学生分数 total += currentScore; // 累加总分 if (currentScore > this.highestScore) { this.highestScore = currentScore; // 更新最高分 } } this.averageScore = total / numOfStudents; // 计算平均分 } // 显示结果 public void displayResults() { // 在显示之前确保统计数据已计算 computeStats(); System.out.println("n--- Student Details ---"); for (Student student : ourClass) { // 使用增强for循环遍历数组 student.writeOutput(); } System.out.println("n--- Class Statistics ---"); System.out.println("Total number of students: " + numOfStudents); System.out.println("Average Score = " + String.format("%.2f", this.averageScore)); // 格式化输出平均分 System.out.println("Highest Score = " + this.highestScore); } // 主方法,用于运行程序 public static void main(String[] args) { TestReporter reporter = new TestReporter(); reporter.getData(); // 获取学生数据 // reporter.computeStats(); // displayResults会调用computeStats,此处可省略 reporter.displayResults(); // 显示结果 } }
3. 完整的示例代码
为了方便读者运行和测试,我们将 Student 类和 TestReporter 类整合,并提供一个 main 方法作为程序的入口。
Student.java
import java.util.Scanner; public class Student { private String name; private int score; public Student() { } public Student(String name, int score){ this.name = name; this.score = score; } public void setName(String name) { this.name = name; } public void setScore(int score) { this.score = score; } public String getName() { return this.name; } public int getScore() { return this.score; } public void readInput() { Scanner keyboard = new Scanner(System.in); // 注意:实际应用中Scanner的管理 System.out.print(" Enter student's name: "); this.name = keyboard.next(); System.out.print(" Enter student's score: "); this.score = keyboard.nextInt(); // keyboard.close(); // 不在此处关闭 } public void writeOutput() { System.out.println("Name: " + name + ", Score: " + score + "%"); } }
TestReporter.java
import java.util.Scanner; public class TestReporter { private int highestScore; private double averageScore; private Student[] ourClass; private int numOfStudents; public TestReporter(){ this.highestScore = 0; // 初始化为0,假设分数非负 this.averageScore = 0.0; this.numOfStudents = 0; } public void getData() { Scanner keyboard = new Scanner(System.in); System.out.println("Please enter the number of students:"); numOfStudents = keyboard.nextInt(); ourClass = new Student[numOfStudents]; for (int i = 0; i < numOfStudents ; i++) { ourClass[i] = new Student(); System.out.println("n--- Entering data for Student " + (i + 1) + " ---"); ourClass[i].readInput(); } // keyboard.close(); // 不在此处关闭 } public void computeStats() { if (numOfStudents == 0 || ourClass == null) { System.out.println("No student data available to compute stats."); this.averageScore = 0.0; this.highestScore = 0; return; } double total = 0; this.highestScore = 0; // 重新初始化,确保每次计算都是从头开始 for (int i = 0; i < numOfStudents; i++) { int currentScore = ourClass[i].getScore(); total += currentScore; if (currentScore > this.highestScore) { this.highestScore = currentScore; } } this.averageScore = total / numOfStudents; } public void displayResults() { computeStats(); // 确保在显示前计算统计数据 System.out.println("n--- Student Details ---"); for (Student student : ourClass) { student.writeOutput(); } System.out.println("n--- Class Statistics ---"); System.out.println("Total number of students: " + numOfStudents); System.out.println("Average Score = " + String.format("%.2f", this.averageScore)); System.out.println("Highest Score = " + this.highestScore); } public static void main(String[] args) { TestReporter reporter = new TestReporter(); reporter.getData(); reporter.displayResults(); } }
4. 注意事项与最佳实践
- Getter/Setter 的正确用法: Getter 方法用于获取私有成员变量的值,不应接受参数。Setter 方法用于设置私有成员变量的值,通常接受一个与成员变量类型相同的参数。
- 变量初始化: 在计算最大值时,highestScore 应被初始化为一个足够小的值(例如 0 对于非负分数,或 Integer.MIN_VALUE 对于可能包含负数的情况),以确保任何有效分数都能被正确识别为最大值。
- 循环遍历对象数组: 使用传统的 for 循环(通过索引)或增强 for 循环(for (Student student : ourClass))都是有效的遍历方式。当需要访问数组索引时,传统 for 循环更合适;当仅需遍历元素时,增强 for 循环更简洁。
- 职责分离: TestReporter 类很好地体现了职责分离的原则。getData 负责数据输入,computeStats 负责数据计算,displayResults 负责数据输出。这种模块化设计提高了代码的可读性和可维护性。
- Scanner 资源管理: 在实际生产环境中,Scanner 对象应谨慎管理。频繁地创建和关闭 Scanner 可能会导致资源泄露或异常。通常建议在应用程序生命周期中只创建一次,并在程序结束时统一关闭。在本教程示例中,为了简化,readInput 和 getData 方法内部都创建了 Scanner,但在大型应用中应避免。
- 错误处理: 对于用户输入,应考虑添加错误处理机制,例如使用 try-catch 块来处理 InputMismatchException,以防止用户输入非数字字符导致程序崩溃。
总结
通过本教程,我们学习了如何在Java中有效地处理包含多种数据类型的对象数组。关键在于正确设计自定义类(如 Student),确保 Getter 和 Setter 方法的规范性,并通过遍历数组来访问对象的特定属性,进而执行统计计算。清晰的模块化设计和对细节(如变量初始化、资源管理)的关注是编写健壮、可维护代码的重要方面。掌握这些技能对于任何需要进行数据处理的java开发者都至关重要。
评论(已关闭)
评论已关闭