Java Calendar.getTime()返回值意外变更问题排查
问题原因
Calendar实例的值变动和读取日期字段的操作没有任何关系,问题出在day_list.setSelectedIndex(d-1)这行代码触发了意料之外的监听器逻辑。
- 你已经给
day_list下拉框绑定了change_date动作监听器,Swing的JComboBox只要执行setSelectedIndex修改选中项,无论触发源是用户手动点击还是代码主动调用,都会立刻触发绑定的所有ActionListener执行。 - 你写的
change_date监听器本身存在逻辑错误:
private class change_date implements ActionListener { public void actionPerformed(ActionEvent ae) { JComboBox b = (JComboBox) ae.getSource(); calendar.set(Calendar.DAY_OF_MONTH, b.getSelectedIndex()); } }
下拉框索引从0开始计数,你填充的选项是1到当月最大天数的整数,索引0对应1号、索引1对应2号,这里直接把索引值作为日期传给Calendar.DAY_OF_MONTH,本身就比正确日期小1;如果索引值为0(对应1号),Calendar会自动把日期计算为上个月的最后一天,直接出现跨月的异常跳变。
执行链路还原
点击"Get Current Date"按钮时的执行顺序完全可以解释两次打印结果不一致的现象:
- 执行
calendar.setTime(new Date())将日历对象重置为当前时间,第一次打印出正确的当前日期 - 读取当前日期的“日”字段值d,调用
day_list.setSelectedIndex(d-1)同步下拉框选中状态 - 该调用立刻触发
change_date监听器,执行错误的calendar.set逻辑修改了日历对象存储的时间 - 执行第二次打印,此时拿到的已经是被监听器改坏的日期值
修复方法
- 修正监听器的取值逻辑,不要用索引计算日期,直接取下拉框中存储的实际日期值:
private class change_date implements ActionListener { public void actionPerformed(ActionEvent ae) { JComboBox<Integer> b = (JComboBox<Integer>) ae.getSource(); calendar.set(Calendar.DAY_OF_MONTH, b.getSelectedItem()); } }
- 代码主动修改下拉框选中状态时,如果不需要触发联动逻辑,可以临时移除监听器,修改完成后再重新绑定,避免非用户操作产生的额外修改。更规范的实现是把监听器实例存为类的成员变量,不需要每次动态获取或新建:
// 把监听器定义为成员变量 private final ActionListener dateChangeListener = new change_date(); // 绑定监听器时直接用成员变量 day_list.addActionListener(dateChangeListener); private void set_calendar() { System.out.println(calendar.getTime()); int d = calendar.get(Calendar.DAY_OF_MONTH); // 临时移除监听器,避免触发修改 day_list.removeActionListener(dateChangeListener); day_list.setSelectedIndex(d-1); // 重新绑定监听器 day_list.addActionListener(dateChangeListener); System.out.println(calendar.getTime()); }
内容的提问来源于stack exchange,提问作者user19356087
相关产品推荐
相关产品推荐

