風起Java Date
令天問了同事有咩有對Date源碼瞭解,同事說沒有了解過。作爲日期工具裏面的常用選手Date,大概也是不會滿意這番回答,那我們接下來就對這位健將做一番專訪,讓我們重新認識java.util 包提供了 Date 類吧。
雨來
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
}
先讓我們看下打印結果
Tue Jan 14 14:51:54 CST 2020
不應該輸出Date的類的信息嗎?爲毛這樣的呢,看來我們還得繼續深入Date這位老將,一層層的揭開它的迷霧。
霧來
/**
* Allocates a <code>Date</code> object and initializes it so that
* it represents the time at which it was allocated, measured to the
* nearest millisecond.
*
* @see java.lang.System#currentTimeMillis()
*/
public Date() {
this(System.currentTimeMillis());
}
/**
* Allocates a <code>Date</code> object and initializes it to
* represent the specified number of milliseconds since the
* standard base time known as "the epoch", namely January 1,
* 1970, 00:00:00 GMT.
*
* @param date the milliseconds since January 1, 1970, 00:00:00 GMT.
* @see java.lang.System#currentTimeMillis()
*/
public Date(long date) {
fastTime = date;
}
至此,我們看到Date的構造方法還是調用了System.currentTimeMillis()系統時間,最後對fastTime 進行了賦值就完了,可爲什麼結果是字符串呢,既然Date本身沒有問題,那麼就是System.out.println()從中做了我們不知的事情,看來源碼之路路漫漫繼續吧。
/**
* Prints an Object and then terminate the line. This method calls
* at first String.valueOf(x) to get the printed object's string value,
* then behaves as
* though it invokes <code>{@link #print(String)}</code> and then
* <code>{@link #println()}</code>.
*
* @param x The <code>Object</code> to be printed.
*/
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
似乎真相快要浮出水面了,讓我們看下 String.valueOf(x)做了什麼吧
/**
* Returns the string representation of the {@code Object} argument.
*
* @param obj an {@code Object}.
* @return if the argument is {@code null}, then a string equal to
* {@code "null"}; otherwise, the value of
* {@code obj.toString()} is returned.
* @see java.lang.Object#toString()
*/
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
/**
* Converts this <code>Date</code> object to a <code>String</code>
* of the form:
* <blockquote><pre>
* dow mon dd hh:mm:ss zzz yyyy</pre></blockquote>
* where:<ul>
* <li><tt>dow</tt> is the day of the week (<tt>Sun, Mon, Tue, Wed,
* Thu, Fri, Sat</tt>).
* <li><tt>mon</tt> is the month (<tt>Jan, Feb, Mar, Apr, May, Jun,
* Jul, Aug, Sep, Oct, Nov, Dec</tt>).
* <li><tt>dd</tt> is the day of the month (<tt>01</tt> through
* <tt>31</tt>), as two decimal digits.
* <li><tt>hh</tt> is the hour of the day (<tt>00</tt> through
* <tt>23</tt>), as two decimal digits.
* <li><tt>mm</tt> is the minute within the hour (<tt>00</tt> through
* <tt>59</tt>), as two decimal digits.
* <li><tt>ss</tt> is the second within the minute (<tt>00</tt> through
* <tt>61</tt>, as two decimal digits.
* <li><tt>zzz</tt> is the time zone (and may reflect daylight saving
* time). Standard time zone abbreviations include those
* recognized by the method <tt>parse</tt>. If time zone
* information is not available, then <tt>zzz</tt> is empty -
* that is, it consists of no characters at all.
* <li><tt>yyyy</tt> is the year, as four decimal digits.
* </ul>
*
* @return a string representation of this date.
* @see java.util.Date#toLocaleString()
* @see java.util.Date#toGMTString()
*/
public String toString() {
// "EEE MMM dd HH:mm:ss zzz yyyy";
BaseCalendar.Date date = normalize();
StringBuilder sb = new StringBuilder(28);
int index = date.getDayOfWeek();
if (index == BaseCalendar.SUNDAY) {
index = 8;
}
convertToAbbr(sb, wtb[index]).append(' '); // EEE
convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd
CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
TimeZone zi = date.getZone();
if (zi != null) {
sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
} else {
sb.append("GMT");
}
sb.append(' ').append(date.getYear()); // yyyy
return sb.toString();
}
雲散霧開
至此真相大白於天下,打印一個類對象時會自動調用toString方法,就出現了最後的結果。當然在我們揭祕Date的同時,也離不開fastTime和cdate兩位夥伴的幫助。
private transient long fastTime;
/*
* If cdate is null, then fastTime indicates the time in millis.
* If cdate.isNormalized() is true, then fastTime and cdate are in
* synch. Otherwise, fastTime is ignored, and cdate indicates the
* time.
*/
private transient BaseCalendar.Date cdate;
特別提名友情出演來自於sun.util.calendar的BaseCalendar。
public abstract class BaseCalendar extends AbstractCalendar {
public static final int JANUARY = 1;
public static final int FEBRUARY = 2;
public static final int MARCH = 3;
public static final int APRIL = 4;
public static final int MAY = 5;
public static final int JUNE = 6;
public static final int JULY = 7;
public static final int AUGUST = 8;
public static final int SEPTEMBER = 9;
public static final int OCTOBER = 10;
public static final int NOVEMBER = 11;
public static final int DECEMBER = 12;
public static final int SUNDAY = 1;
public static final int MONDAY = 2;
public static final int TUESDAY = 3;
public static final int WEDNESDAY = 4;
public static final int THURSDAY = 5;
public static final int FRIDAY = 6;
public static final int SATURDAY = 7;
private static final int BASE_YEAR = 1970;
...
}
醫理上常說“痛則不通,通則不痛”,學問亦是如此,不通則不明,正所謂一理通而百理明。
番外
在我們debug的路途中發現初始時間是1970年1月1日,爲什麼這樣呢,其實不僅僅是Java,幾乎所有的語言的時間都是從這一刻開始算起的。因爲java起源於UNIX系統,而UNIX認爲1970年1月1日0點是時間紀元。所有有了我們看到的結果。