关于Calendar中的set操作的理解

前因:在学习Calendar类的相关操作时发现,可以通过该类的set()方法设置日期时间的某一部分,查看源码发现,相应的日期时间变量全都是static final类型变量,即静态常量,觉得很不可思议。
下面写写关于我查阅资料后的一些理解,希望对看官们有所帮助:
  • 先上API中关于Calendar属性变量的定义:
    Calendar的API
    可以发现很多字段都被static修饰,点开其中一个进行查看,如下:
    Calendar类属性说明

从截图可以发现,所有的关于日期/时间的属性都设置为了static final,即常量,那么为什么我们可以通过set()对Calendar的日期时间进行设置呢?

  • 于是,这一次我们需要查看的是set()方法的源码:
    部分源码如下:
/**
     * 这里是方法说明
     * Sets the given calendar field to the given value. The value is not
     * interpreted by this method regardless of the leniency mode.
     *
     * @param field the given calendar field.
     * @param value the value to be set for the given calendar field.
     * @throws ArrayIndexOutOfBoundsException if the specified field is out of range
     *             (<code>field &lt; 0 || field &gt;= FIELD_COUNT</code>).
     * in non-lenient mode.
     * @see #set(int,int,int)
     * @see #set(int,int,int,int,int)
     * @see #set(int,int,int,int,int,int)
     * @see #get(int)
     */
    public void set(int field, int value)
    {
        // If the fields are partially normalized, calculate all the
        // fields before changing any fields.
        if (areFieldsSet && !areAllFieldsSet) {
            computeFields();
        }
        internalSet(field, value);  //!!!重点看这里!!!
        isTimeSet = false;
        areFieldsSet = false;
        isSet[field] = true;
        stamp[field] = nextStamp++;
        if (nextStamp == Integer.MAX_VALUE) {
            adjustStamp();
        }
    }

源码比较复杂,涉及的底层有很多,我们这里只看最核心的一句,就是internalSet(field,value),很明显是调用了其他的方法进行修改值,所以下一步,我们查看internalSet(field,value)方法的源码:

/**
     * Sets the value of the given calendar field. This method does
     * not affect any setting state of the field in this
     * <code>Calendar</code> instance.
     *
     * @throws IndexOutOfBoundsException if the specified field is out of range
     *             (<code>field &lt; 0 || field &gt;= FIELD_COUNT</code>).
     * @see #areFieldsSet
     * @see #isTimeSet
     * @see #areAllFieldsSet
     * @see #set(int,int)
     */
    final void internalSet(int field, int value)
    {
        fields[field] = value;
    }

从这个方法的源码我们可以看出,我们一开始调用set(field,value)时,第一个参数是作为fields数组的索引来用,第二个参数才是我们的目标值,那么这个fields数组到底是何方神圣呢?上源码:

/**
     * The calendar field values for the currently set time for this calendar.
     * This is an array of <code>FIELD_COUNT</code> integers, with index values
     * <code>ERA</code> through <code>DST_OFFSET</code>.
     * @serial
     */
    @SuppressWarnings("ProtectedField")
    protected int           fields[];

从源码的文档说明来看,fields保存的就是当前设置时间的日历字段值。
什么意思?很简单,下面划重点!!!
意思就是说,fields存储了我们这个日历的真实内容,包括日期和时间,是如何保存的呢?是将我们的日期时间进行分段保存在数组里,如:
YEAR    MONTH     DAY_OF_MONTH …
    1              2                    5 …
通过将我们的日历进行分段,年为一个段,月为一个段……,最终一个段保存在数组的一个位置上。这就是fields存储日历的原理。


那么接下来解释为什么我们可以通过set()来修改日历值:

  • 先想第一个问题:我们修改日历的某一部分,是不是实际上是修改fileds数组上某个元素的值,而通过源码我们知道,fileds数组是没有被final修饰的,因此是可以修改的。
  • 那么第二个问题:我们是如何定位到fileds数组的某个具体位置上进行值的修改的呢?
    其实就是通过set(参数1,参数2)中的第1个参数。
    假设“年”存储在数组的第一个元素上,那么一般而言,我们修改的时候,应该是这样输入:set(0,2021),其中0表示要修改的数组元素的位置,2021表示具体想修改成的值。
    但对我们开发者而言,要记住日历的每个段在数组上的索引并不容易,因此API在提供时,对索引进行了命名,也就是利用了静态常量,如static final YEAR = 0;这种方式,从而达到见名知义的效果,方便开发者调用API,所以才会出现了那么多的静态常量属性。

好了,综上所述呢,我们的静态常量其实并不是我们想修改的变量,我们是将静态常量作为数组的索引,由于静态常量不会被改变,所以数组上的固定位置不会被改变,当我们想修改某个日历中的段时,通过静态常量去定位到实际保存日历段的数组fields的某个具体位置,对元素值进行修改,从而达到我们的目的。这也就是为什么set()需要两个参数,而get()可以通过指定参数得到我们需要的数据的原因,底层都是通过静态常量对fileds数组进行操作的!


核心结论:日历存储时,利用了key-value方式,其中key对应了Calendar中的静态常量,不可被修改;value对应了fileds[ ]中某个具体位置上的值,可以被修改,通过key与某个具体位置进行关联。


以上内容纯属个人理解,如有错误,还望海涵!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值