问题描述
完全公开,这是作业,但只是作业的一小部分,我无法弄清楚为什么它不起作用。这是作业文本:
创建一个 CollegeStudent 类。这个类将包含数据字段 包含学生的名字、姓氏、注册日期和 预计毕业日期,使用 GregorianCalendar 类为每个 日期。为每个字段提供 get() 和 set() 方法。还提供一个 需要名字和姓氏以及注册日期的构造函数, 并将项目毕业日期设置为恰好四年后 注册。将类保存为 CollegeStudent.java。
创建一个交互式应用程序,提示用户输入两个数据 大学生对象。提示用户输入名字、姓氏、 每个的注册月份、注册日和注册年份 CollegeStudent,然后实例化对象。显示所有 值,包括预计的毕业日期。保存 应用程序作为 TestCollegeStudent.java
我已经能够做它要求的所有事情,除了当我尝试计算毕业日期时,它也会在注册日期值上增加 4 年。这是我的大学生课程:
import java.util.GregorianCalendar;
public class CollegeStudent {
String firstName;
String lastName;
GregorianCalendar enrollmentDate;
GregorianCalendar graduationDate;
// Constructor requiring first name,last name,and enrollment date
public CollegeStudent(String first,String last,GregorianCalendar enrollment) {
firstName = first;
lastName = last;
enrollmentDate = enrollment;
// Set graduationDate to enrollmentDate,then add 4 years
graduationDate = enrollment;
graduationDate.add(GregorianCalendar.YEAR,4);
}
// Get and Set firstName
public void setFirstName(String first) {
firstName = first;
}
public String getFirstName() {
return firstName;
}
// Get and Set lastName
public void setLastName(String last) {
lastName = last;
}
public String getLastName() {
return lastName;
}
// Get and Set enrollmentDate
public void setEnrollmentDate(GregorianCalendar enrollment) {
enrollmentDate = enrollment;
}
public GregorianCalendar getEnrollmentDate(){
return enrollmentDate;
}
// Get and Set graduationDate
public void setGraduationDate(GregorianCalendar graduation) {
graduationDate = graduation;
}
public GregorianCalendar getGraduationDate() {
return graduationDate;
}
}
我的 TestCollegeStudent.java 显示了注册日期和毕业日期,并且它们都显示了用户输入的初始注册日期 4 年后的相同日期。如果删除将 4 年添加到毕业日期的行,它们都显示为初始注册日期。我觉得我遗漏了一些非常简单的语法问题,但我一直无法弄清楚它是什么。
解决方法
这是由于构造函数中的以下赋值而发生的:
graduationDate = enrollment;
此赋值使 graduationDate
引用 Calendar
正在被 enrollment
引用的同一个实例,因此通过任何引用带来的实例更改将导致结果为两个引用都相同。
你可以通过下面的例子来理解:
import java.util.Calendar;
import java.util.GregorianCalendar;
public class Main {
public static void main(String[] args) {
Calendar enrollment = Calendar.getInstance();
Calendar graduationDate = enrollment;
System.out.println(enrollment.getTime());
System.out.println(graduationDate.getTime());
graduationDate.add(GregorianCalendar.YEAR,4);
System.out.println(enrollment.getTime());
System.out.println(graduationDate.getTime());
}
}
输出:
Sat Jan 16 15:31:29 GMT 2021
Sat Jan 16 15:31:29 GMT 2021
Thu Jan 16 15:31:29 GMT 2025
Thu Jan 16 15:31:29 GMT 2025
为了处理这种情况,graduationDate
需要引用被引用实例的副本(克隆)enrollment
以便通过引用带来的任何更改,graduationDate
只会适用于克隆。
import java.util.Calendar;
import java.util.GregorianCalendar;
public class Main {
public static void main(String[] args) {
Calendar enrollment = Calendar.getInstance();
Calendar graduationDate = (Calendar)enrollment.clone();// You have to do this
System.out.println(enrollment.getTime());
System.out.println(graduationDate.getTime());
graduationDate.add(GregorianCalendar.YEAR,4);
System.out.println(enrollment.getTime());
System.out.println(graduationDate.getTime());
}
}
输出:
Sat Jan 16 15:30:34 GMT 2021
Sat Jan 16 15:30:34 GMT 2021
Sat Jan 16 15:30:34 GMT 2021
Thu Jan 16 15:30:34 GMT 2025
顺便说一下,java.util
的日期时间 API 及其格式化 API SimpleDateFormat
已经过时且容易出错。建议完全停止使用它们并切换到 modern date-time API。
- 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7。
- 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。
查看 Trail: Date Time 以获取使用 java.time API(现代日期时间 API)的分步教程和示例。
,问题是,当您执行 graduationDate = enrollment
时,毕业日期现在指向注册对象,并且您对其中任何一个所做的每个更改都会反映在另一个对象上,因为它们相同 对象。
您真正想要的是创建一个基于另一个日历的新日历,它应该是 graduationDate = (GregorianCalendar)enrollment.clone();
此线程中已经有一个很好的答案。我正在添加我的贡献。问题的主要来源是这行,其中毕业日期指的是入学
<table class="row">
<tr>
<td class="column"></td>
<td class="column"></td>
<td class="column"></td>
</tr>
</table>
你也可以试试这个:
graduationDate = enrollment
,
java.time
对于你的作业,它可能不会去,但其他人都应该使用 java.time,现代 Java 日期和时间 API,用于日期工作。 而且!使用 java.time 几乎不可能出现您所询问的错误。
LocalDate enrollmentDate = LocalDate.of(2021,Month.JANUARY,4);
LocalDate graduationDate = enrollmentDate.plusYears(4);
System.out.format("Enrolled: %s. Projected graduation: %s.%n",enrollmentDate,graduationDate);
输出为:
注册时间:2021-01-04。预计毕业时间:2025-01-04。
走向需求的边界
如果你想教你的老师如何做到这一点,并且仍然保持字段类型为 GregorianCalendar
的要求...... (1) 我不能说这在你的学校是否可取。 (2) 将是一个折衷,即没有理想的解决方案,既有java.time的优点,又有API混合和不必要的转换的缺点。这是:
public class CollegeStudent {
String firstName;
String lastName;
GregorianCalendar enrollmentDate;
GregorianCalendar graduationDate;
// Constructor requiring first name,last name,and enrollment date
public CollegeStudent(String first,String last,int enrollmentYear,int enrollmentMonth,int enrollmentDay) {
firstName = first;
lastName = last;
ZonedDateTime enrollmentZdt
= LocalDate.of(enrollmentYear,enrollmentMonth,enrollmentDay)
.atStartOfDay(ZoneId.systemDefault());
enrollmentDate = GregorianCalendar.from(enrollmentZdt);
// Add 4 years to enrollmentZdt,then convert so it can be assigned to graduationDate
ZonedDateTime graduationZdt = enrollmentZdt.plusYears(4);
graduationDate = GregorianCalendar.from(graduationZdt);
}
// Getters and setters …
}
尝试一下:
CollegeStudent student = new CollegeStudent("Wes","Hampton",2021,1,16);
System.out.println("Enrolled: " + student.getEnrollmentDate().toZonedDateTime());
System.out.println("Will graduate: " + student.getGraduationDate().toZonedDateTime());
在我的时区输出:
Enrolled: 2021-01-16T00:00+01:00[Europe/Copenhagen]
Will graduate: 2025-01-16T00:00+01:00[Europe/Copenhagen]
链接
- 这会让你的老师好好读一读。 Oracle tutorial: Date Time 解释如何使用 java.time。
- 一个相关问题:Changing values in an object makes changes to another