
在面向对象设计中,构造函数是创建对象实例的特殊方法。在uml类图中,构造函数的表示方式有两种:
对于本教程中的Student类,我们将遵循Java的约定,将Student(name:String)视为其构造函数。
根据UML图和业务需求,Student类需要一个构造函数来初始化学生姓名,并为家庭作业和考试成绩创建空的数组。家庭作业成绩数组应包含6个元素,考试成绩数组应包含3个元素。
首先,定义Student类的成员变量:
public class Student {
private String name;
private int[] homeworkScores; // 存储家庭作业成绩
private int[] examScores; // 存储考试成绩
// 辅助计算属性
// 注意:homeworkAverage和finalScore通常是计算得出的,而不是作为成员变量直接存储,
// 除非有特定缓存需求。在构造函数中,它们不应被初始化,而应通过方法计算。
// private double homeworkAverage;
// private int finalScore;
}接下来,实现构造函数,负责初始化name以及创建并初始化homeworkScores和examScores数组。
立即学习“Java免费学习笔记(深入)”;
public class Student {
private String name;
private int[] homeworkScores;
private int[] examScores;
/**
* 构造函数,用于创建Student对象。
* 初始化学生姓名,并为家庭作业和考试成绩数组分配内存。
* 数组元素默认初始化为0。
*
* @param name 学生的姓名
*/
public Student(String name) {
this.name = name;
this.homeworkScores = new int[6]; // 6个家庭作业成绩
this.examScores = new int[3]; // 3个考试成绩
}
// ... 其他方法 ...
}在这个构造函数中,我们使用new int[size]语法为数组分配了内存。Java会自动将int类型的数组元素初始化为0,这符合“空分数”的需求。
Student类还需要提供计算家庭作业平均分和最终成绩的方法。
该方法应遍历homeworkScores数组,计算所有分数的总和,然后除以作业数量。
public class Student {
// ... 成员变量和构造函数 ...
/**
* 计算并返回学生的家庭作业平均分。
*
* @return 家庭作业的平均分
*/
public double getHomeworkAverage() {
if (homeworkScores == null || homeworkScores.length == 0) {
return 0.0; // 没有作业或作业为空,平均分为0
}
int sum = 0;
for (int score : homeworkScores) {
sum += score;
}
return (double) sum / homeworkScores.length;
}
// ... 其他方法 ...
}最终成绩的计算规则为:考试1占15%,考试2占25%,考试3占30%,家庭作业平均分占30%。
public class Student {
// ... 成员变量、构造函数和getHomeworkAverage方法 ...
/**
* 计算并返回学生的最终成绩。
* 评分权重:考试1 (15%), 考试2 (25%), 考试3 (30%), 家庭作业平均分 (30%)。
*
* @return 学生的最终成绩(整数)
*/
public int getFinalScore() {
if (examScores == null || examScores.length < 3) {
// 考试成绩不完整,无法计算
return 0; // 或者抛出异常
}
double exam1Weight = 0.15;
double exam2Weight = 0.25;
double exam3Weight = 0.30;
double homeworkWeight = 0.30;
double finalScore = (examScores[0] * exam1Weight) +
(examScores[1] * exam2Weight) +
(examScores[2] * exam3Weight) +
(getHomeworkAverage() * homeworkWeight);
return (int) Math.round(finalScore); // 四舍五入到最近的整数
}
// ... 其他方法 ...
}在设计Java类时,尤其当类内部包含数组或集合等可变对象时,直接通过getter方法返回这些对象的引用或通过setter方法直接接收外部对象的引用存在潜在风险。这会破坏对象的封装性,允许外部代码在不经过对象自身控制的情况下修改其内部状态。
考虑以下问题:
为了避免这些风险,应该采用防御性拷贝(Defensive Copying)策略。
import java.util.Arrays; // 用于数组拷贝
public class Student {
private String name;
private int[] homeworkScores;
private int[] examScores;
// ... 构造函数 ...
/**
* 获取学生姓名。
* @return 学生姓名。
*/
public String getName() {
return name;
}
/**
* 设置学生姓名。
* @param name 新的学生姓名。
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取家庭作业成绩数组的副本,以防止外部直接修改内部数组。
* @return 家庭作业成绩数组的副本。
*/
public int[] getHomeworkScores() {
// 返回数组的副本,而不是原始引用
return Arrays.copyOf(homeworkScores, homeworkScores.length);
}
/**
* 设置家庭作业成绩数组。传入的数组会被拷贝,以防止外部修改。
* @param homeworkScores 新的家庭作业成绩数组。
*/
public void setHomeworkScores(int[] homeworkScores) {
// 接收外部数组的副本,而不是直接使用外部引用
if (homeworkScores != null && homeworkScores.length == 6) {
this.homeworkScores = Arrays.copyOf(homeworkScores, homeworkScores.length);
} else {
// 可以选择抛出异常或根据业务逻辑处理不合法的输入
System.err.println("Invalid homework scores array provided. Expected 6 scores.");
}
}
/**
* 获取考试成绩数组的副本,以防止外部直接修改内部数组。
* @return 考试成绩数组的副本。
*/
public int[] getExamScores() {
// 返回数组的副本,而不是原始引用
return Arrays.copyOf(examScores, examScores.length);
}
/**
* 设置考试成绩数组。传入的数组会被拷贝,以防止外部修改。
* @param examScores 新的考试成绩数组。
*/
public void setExamScores(int[] examScores) {
// 接收外部数组的副本,而不是直接使用外部引用
if (examScores != null && examScores.length == 3) {
this.examScores = Arrays.copyOf(examScores, examScores.length);
} else {
// 可以选择抛出异常或根据业务逻辑处理不合法的输入
System.err.println("Invalid exam scores array provided. Expected 3 scores.");
}
}
// ... getHomeworkAverage() 和 getFinalScore() 方法 ...
}通过使用Arrays.copyOf()方法,我们确保了Student对象内部的数组始终是其私有副本,从而有效保护了对象的状态。
import java.util.Arrays;
public class Student {
private String name;
private int[] homeworkScores; // 存储家庭作业成绩,6个
private int[] examScores; // 存储考试成绩,3个
/**
* 构造函数,用于创建Student对象。
* 初始化学生姓名,并为家庭作业和考试成绩数组分配内存。
* 数组元素默认初始化为0。
*
* @param name 学生的姓名
*/
public Student(String name) {
this.name = name;
this.homeworkScores = new int[6]; // 6个家庭作业成绩
this.examScores = new int[3]; // 3个考试成绩
}
/**
* 获取学生姓名。
* @return 学生姓名。
*/
public String getName() {
return name;
}
/**
* 设置学生姓名。
* @param name 新的学生姓名。
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取家庭作业成绩数组的副本,以防止外部直接修改内部数组。
* @return 家庭作业成绩数组的副本。
*/
public int[] getHomeworkScores() {
return Arrays.copyOf(homeworkScores, homeworkScores.length);
}
/**
* 设置家庭作业成绩数组。传入的数组会被拷贝,以防止外部修改。
* 如果传入的数组不合法(null或长度不为6),则打印错误信息。
* @param homeworkScores 新的家庭作业成绩数组。
*/
public void setHomeworkScores(int[] homeworkScores) {
if (homeworkScores != null && homeworkScores.length == 6) {
this.homeworkScores = Arrays.copyOf(homeworkScores, homeworkScores.length);
} else {
System.err.println("Error: Invalid homework scores array provided. Expected 6 scores.");
}
}
/**
* 获取考试成绩数组的副本,以防止外部直接修改内部数组。
* @return 考试成绩数组的副本。
*/
public int[] getExamScores() {
return Arrays.copyOf(examScores, examScores.length);
}
/**
* 设置考试成绩数组。传入的数组会被拷贝,以防止外部修改。
* 如果传入的数组不合法(null或长度不为3),则打印错误信息。
* @param examScores 新的考试成绩数组。
*/
public void setExamScores(int[] examScores) {
if (examScores != null && examScores.length == 3) {
this.examScores = Arrays.copyOf(examScores, examScores.length);
} else {
System.err.println("Error: Invalid exam scores array provided. Expected 3 scores.");
}
}
/**
* 计算并返回学生的家庭作业平均分。
* 如果没有作业或作业数组为空,则平均分为0。
*
* @return 家庭作业的平均分。
*/
public double getHomeworkAverage() {
if (homeworkScores == null || homeworkScores.length == 0) {
return 0.0;
}
int sum = 0;
for (int score : homeworkScores) {
sum += score;
}
return (double) sum / homeworkScores.length;
}
/**
* 计算并返回学生的最终成绩。
* 评分权重:考试1 (15%), 考试2 (25%), 考试3 (30%), 家庭作业平均分 (30%)。
* 如果考试成绩不完整,返回0。
*
* @return 学生的最终成绩(四舍五入到最近的整数)。
*/
public int getFinalScore() {
if (examScores == null || examScores.length < 3) {
System.err.println("Error: Cannot calculate final score, exam scores are incomplete.");
return 0;
}
double exam1Weight = 0.15;
double exam2Weight = 0.25;
double exam3Weight = 0.30;
double homeworkWeight = 0.30;
double finalScore = (examScores[0] * exam1Weight) +
(examScores[1] * exam2Weight) +
(examScores[2] * exam3Weight) +
(getHomeworkAverage() * homeworkWeight);
return (int) Math.round(finalScore);
}
// 示例主方法,用于测试
public static void main(String[] args) {
Student student1 = new Student("Alice");
System.out.println("Student Name: " + student1.getName());
// 设置家庭作业成绩
int[] aliceHomeworks = {85, 90, 78, 92, 88, 95};
student1.setHomeworkScores(aliceHomeworks);
// 设置考试成绩
int[] aliceExams = {70, 80, 90};
student1.setExamScores(aliceExams);
System.out.println("Alice's Homework Average: " + student1.getHomeworkAverage());
System.out.println("Alice's Final Score: " + student1.getFinalScore());
// 尝试修改通过getter获得的数组 (防御性拷贝会阻止)
int[] hwScores = student1.getHomeworkScores();
if (hwScores.length > 0) {
hwScores[0] = 1000; // 尝试修改
}
System.out.println("After external modification attempt, Alice's Homework Average: " + student1.getHomeworkAverage()); // 平均分不变
// 尝试通过setter传入后修改原数组 (防御性拷贝会阻止)
int[] externalExams = {60, 70, 80};
student1.setExamScores(externalExams);
externalExams[0] = 500; // 尝试修改原数组
System.out.println("After external array modification attempt, Alice's Exam 1 Score (via getter): " + student1.getExamScores()[0]); // 依然是60,不是500
}
}本教程详细演示了如何将UML类图中的设计理念转换为功能健全的Java类。我们从UML构造函数的理解开始,逐步实现了Java构造函数,确保了对象创建时成员变量(特别是数组)的正确初始化。随后,我们根据业务逻辑实现了计算家庭作业平均分和最终成绩的核心方法。
最重要的是,教程强调了在Java中处理可变数组成员时采用防御性拷贝的重要性。通过在getter方法中返回数组的副本,并在setter方法中接收传入数组的副本,我们有效保护了类的内部状态,防止外部代码在未经控制的情况下修改对象数据,从而增强了对象的封装性和健壮性。遵循这些最佳实践,有助于构建更稳定、更易于维护的Java应用程序。
以上就是从UML类图到Java对象:构造函数与数组处理详解的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号