Java 为什么在使用遗留API时,日期比日历更适合属性键入?

Java 为什么在使用遗留API时,日期比日历更适合属性键入?,java,date,calendar,Java,Date,Calendar,首先,请注意,此问题不是此问题的重复: . 我的问题要具体得多。引用的问题问“什么”(或“哪个”),但我已经知道“什么”,并且正在问“为什么” 我所在的团队正在为客户机改进现有Java项目。这个Java项目使用Java6,并且没有Joda时间作为依赖项。询问之后,似乎添加Joda时间或升级到Java8都不是选项 因此,在将日期/时间表示为对象中的字段时,我们必须使用日历或日期来键入属性。这个项目的遗留代码中充斥着使用日历来表示日期/时间字段的对象,这些字段是我们永远无法处理的(比如在添加或减去

首先,请注意,此问题不是此问题的重复: . 我的问题要具体得多。引用的问题问“什么”(或“哪个”),但我已经知道“什么”,并且正在问“为什么”

我所在的团队正在为客户机改进现有Java项目。这个Java项目使用Java6,并且没有Joda时间作为依赖项。询问之后,似乎添加Joda时间或升级到Java8都不是选项


因此,在将日期/时间表示为对象中的字段时,我们必须使用日历或日期来键入属性。这个项目的遗留代码中充斥着使用日历来表示日期/时间字段的对象,这些字段是我们永远无法处理的(比如在添加或减去时间单位时,等等)。我知道这是一种不好的做法,因为日历是一个更复杂的对象,而日期更简单,也同样有效。(当然,我知道这两个都是很长一段时间内的基本包装,都是可变的,设计也很差,但这也是我们仅有的两个选择。)

换句话说,像这样的对象:

public class Reservation {

    private Guest guest;
    // Set only once, never used for calculations
    private Calendar dateReserved;
    ...
}
应该是这样的:

public class Reservation {

    private Guest guest;
    // Set only once, never used for calculations
    private Date dateReserved;
    ...
}
然后我注意到,当为新功能添加新对象时,我的团队遵循同样的惯例,使用日历而不是日期。当我提出这个问题时,我的回答是最好使用Calendar,因为它可以做更多的事情,并且不像Date那样有所有这些不推荐的方法

我知道这个理由过于简单化了。我还看到对于更广泛的使用问题表达了相同的观点,即日历不应用于属性键入。然而,答案并没有包含太多关于为什么不应该首选日历的解释


所以我已经知道“什么”了。但我想向我的团队说明情况,所以我的问题是,“为什么”?为什么在键入属性时,日期应优先于日历?使用日历而不是日期键入属性有什么缺点?

日期对象比日历对象具有更少的字段和占用更少的内存,并且实例化速度更快

我同意Jon Skeet关于日历系统和时区的评论,我认为你的前提存在根本性缺陷。日期并不比日历好。如果你从来没有比较过时间,或者从来没有在不同的时区有过两个日期,那么当然,我想更小的内存占用可能会更好,但在这一点上,只需要使用long和Unix时间戳。日历是迄今为止更好的对象模型,毕竟,如果您确实需要它,您可以从中获取日期对象。

如果您在属性键入时不得不在日期和日历之间进行选择,请: 如果以下任一项为真,则使用日历:

  • 初始设置日期/时间后,您需要能够调整日期/时间 (例如更改月份,同时保持日期和时间不变)

  • 你需要知道时区

否则,出于以下原因使用日期:

  • 准确地表达你的意图。如果你使用日历,你是在暗示你想要一个你实际上并不打算使用的功能(时区、更改日期或月份等)
  • 减少了字符串表示的麻烦。例如,考虑这个类:

    public class Reservation {
        private Guest guest;
        private Calendar dateReserved;
    
        @Override
        public String toString() {
            return String.format("Reservation{guest=%s,dateReserved=\"%s\"}",
                    guest, dateReserved);
        }
    }
    
    现在,如果您打印出这个类的一个实例,您将得到一些可怕的结果:

    Reservation{guest=guest{id=17,name=“John Smith”},dateReserved=“java.util.GregorianCalendar[time=1426707020619,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id=“America/LosèAngeles”,offset=-28800;=3600000,dstaving=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-2880000,dstSaves=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDayOfWeek=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],周的第一天=1,周的第一天=1,纪元=1,年=2015,月=2,年的周=12,月的周=3,月的日=18,年的日=77,周的日=4,月的周的日=3,上午下午=1,小时=0,日的小时=12,分钟=30,秒=20,毫秒=619,分区偏移量=-288000000,DST偏移量=3600000


    然而,如果您使用Date,则会得到以下结果:

    预订{guest=guest{id=17,name=“John Smith”},dateReserved=“Wed Mar 18 12:34:26 PDT 2015”}


    因此,如果您使用Calendar并且希望toString()可用,则需要调用dateReserved.getTime()--这意味着您需要添加一个空检查。这涉及到是否最终使用DateFormat对象

  • Date是一个更小的对象,实例化更快,开销更少

  • Date实际上是不可变的——这意味着更改Date对象的唯一方法是使用不推荐的方法。因此,正如第1点所述,表达您的意图很重要。如果您的Date字段应该是不可变的,请不要混淆将来将使用Calendar来更改代码的开发人员(当然,除非您需要时区意识)

  • 对于表示单个时间点的字段类型,“日期”是比“日历”更直观的名称


  • “我知道两者基本上是同一个概念”-嗯,不,它们不是。日期是一个时间点,没有相关的日历系统或时区。
    日历有一个日历系统,根据其具体类别(例如Gregorianalendar)并且可以设置为任何时区。你对哪个时区感兴趣,你希望如何向
    Date
    解释这一点?@JonSkeet你是对的,我应该澄清一下——我的意思是,我知道他们都是通过包装一个很长的历元毫秒来完成任务的。