下面的程序演練了Date和Calendar類的某些基本特性,它會打印出什么呢?
import java.util.*;
public class DatingGame {
public static void main(String[ ] args) {
Calendar cal = Calendar.getInstance();
cal.set(1999, 12, 31); // Year, Month, Day
System.out.print(cal.get(Calendar.YEAR) + " ");
Date d = cal.getTime();
System.out.println(d.getDay());
}
}
該程序創(chuàng)建了一個Calendar實例,它應(yīng)該表示的是1999年的除夕夜,然后該程序打印年份和日??雌饋碓摮绦驊?yīng)該打印1999 31,但是它沒有;它打印的是2000 1。難道這是致命的Y2K(千年蟲)問題嗎?
不,事情比我們想象的要糟糕得多:這是致命的Date/Calendar問題。在Java平臺首次發(fā)布時,它支持日歷計算類的就是Date類。這個類在能力方面是受限的,特別是當(dāng)需要支持國際化時,它就暴露出了一個基本的設(shè)計缺陷:Date實例是易變的。在1.1版中,Calendar類被添加到了Java平臺中,以矯正Date的缺點,由此大部分的Date方法就都被棄用了。遺憾的是,這么做只能使情況更糟。我們的程序說明Date和Calendar API有許多問題。
該程序的第一個bug就位于方法調(diào)用cal.set(1999,12,31)中。當(dāng)月份以數(shù)字來表示時,習(xí)慣上我們將第一個月被賦值為1。遺憾的是,Date將一月表示為0,而Calendar延續(xù)了這個錯誤。因此,這個方法調(diào)用將日歷設(shè)置到了1999年第13個月的第31天。但是標(biāo)準(zhǔn)的(西歷)日歷只有12個月,該方法調(diào)用肯定應(yīng)該拋出一個IllegalArgumentException異常,對嗎?它是應(yīng)該這么做,但是它并沒有這么做。Calendar類直接將其替換為下一年,在本例中即2000年的第一個月。這也就解釋了我們的程序為什么打印出的第一個數(shù)字是2000。
有兩種方法可以訂正這個問題。你可以將cal.set調(diào)用的第二個參數(shù)由12改為11,但是這么做容易引起混淆,因為數(shù)字11會讓讀者誤以為是11月。更好的方式是使用Calendar專為此目的而定義的常量,即Calendar.DECEMBER。
該程序打印出的第二個數(shù)字又是怎么回事呢?cal.set調(diào)用很明顯是要把日歷設(shè)置到這個月的第31天,Date實例d表示的是與Calendar相同的時間點,因此它的getDay方法應(yīng)該返回31,但是程序打印的卻是1,這是怎么搞得呢?
為了找出原因,你必須先閱讀一下文檔,它敘述道Date.getDay返回的是Date實例所表示的星期日期,而不是月份日期。這個返回值是基于0的,從星期天開始計算。因此程序所打印的1表示2000年1月31日是星期一。請注意,相應(yīng)的Calendar方法get(Calendar.DAY_OF_WEEK) 不知為什么返回的是基于1的星期日期值,而不是像Date的對應(yīng)方法那樣返回基于0的星期日期值。
有兩種方法可以訂正這個問題。你可以調(diào)用Date.date這一名字極易讓人混淆的方法,它返回的是月份日期。然而,與大多數(shù)Date方法一樣,它已經(jīng)被棄用了,因此你是將Date徹底拋棄,直接調(diào)用Calendar的get(Calendar.DAY_OF_MONTH)方法。用這兩種方法,該程序都可以打印出我們想要的1999 31:
public class DatingGame {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(1999, Calendar.DECEMBER, 31);
System.out.print(cal.get(Calendar.YEAR) + " ");
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
}
}
本謎題只是掀開了Calendar和Date缺陷的冰山一角。這些API簡直就是雷區(qū)。Calendar其他的嚴(yán)重問題包括弱類型(幾乎每樣事物都是一個int)、過于復(fù)雜的狀態(tài)空間、拙劣的結(jié)構(gòu)、不一致的命名以及不一致的雨衣等。在使用Calendar和Date的時候一定要當(dāng)心,千萬要記著查閱API文檔。
對API設(shè)計者來說,其教訓(xùn)是:如果你不能在第一次設(shè)計時就使它正確,那么至少應(yīng)該在第二次設(shè)計時應(yīng)該使它正確,絕對不能留到第三次設(shè)計時去處理。如果你對某個API的首次嘗試出現(xiàn)了嚴(yán)重問題,那么你的客戶可能會原諒你,并且會再給你一次機(jī)會。如果你第二次嘗試又有問題,你可能會永遠(yuǎn)堅持這些錯誤了。
import java.util.*;
public class DatingGame {
public static void main(String[ ] args) {
Calendar cal = Calendar.getInstance();
cal.set(1999, 12, 31); // Year, Month, Day
System.out.print(cal.get(Calendar.YEAR) + " ");
Date d = cal.getTime();
System.out.println(d.getDay());
}
}
該程序創(chuàng)建了一個Calendar實例,它應(yīng)該表示的是1999年的除夕夜,然后該程序打印年份和日??雌饋碓摮绦驊?yīng)該打印1999 31,但是它沒有;它打印的是2000 1。難道這是致命的Y2K(千年蟲)問題嗎?
不,事情比我們想象的要糟糕得多:這是致命的Date/Calendar問題。在Java平臺首次發(fā)布時,它支持日歷計算類的就是Date類。這個類在能力方面是受限的,特別是當(dāng)需要支持國際化時,它就暴露出了一個基本的設(shè)計缺陷:Date實例是易變的。在1.1版中,Calendar類被添加到了Java平臺中,以矯正Date的缺點,由此大部分的Date方法就都被棄用了。遺憾的是,這么做只能使情況更糟。我們的程序說明Date和Calendar API有許多問題。
該程序的第一個bug就位于方法調(diào)用cal.set(1999,12,31)中。當(dāng)月份以數(shù)字來表示時,習(xí)慣上我們將第一個月被賦值為1。遺憾的是,Date將一月表示為0,而Calendar延續(xù)了這個錯誤。因此,這個方法調(diào)用將日歷設(shè)置到了1999年第13個月的第31天。但是標(biāo)準(zhǔn)的(西歷)日歷只有12個月,該方法調(diào)用肯定應(yīng)該拋出一個IllegalArgumentException異常,對嗎?它是應(yīng)該這么做,但是它并沒有這么做。Calendar類直接將其替換為下一年,在本例中即2000年的第一個月。這也就解釋了我們的程序為什么打印出的第一個數(shù)字是2000。
有兩種方法可以訂正這個問題。你可以將cal.set調(diào)用的第二個參數(shù)由12改為11,但是這么做容易引起混淆,因為數(shù)字11會讓讀者誤以為是11月。更好的方式是使用Calendar專為此目的而定義的常量,即Calendar.DECEMBER。
該程序打印出的第二個數(shù)字又是怎么回事呢?cal.set調(diào)用很明顯是要把日歷設(shè)置到這個月的第31天,Date實例d表示的是與Calendar相同的時間點,因此它的getDay方法應(yīng)該返回31,但是程序打印的卻是1,這是怎么搞得呢?
為了找出原因,你必須先閱讀一下文檔,它敘述道Date.getDay返回的是Date實例所表示的星期日期,而不是月份日期。這個返回值是基于0的,從星期天開始計算。因此程序所打印的1表示2000年1月31日是星期一。請注意,相應(yīng)的Calendar方法get(Calendar.DAY_OF_WEEK) 不知為什么返回的是基于1的星期日期值,而不是像Date的對應(yīng)方法那樣返回基于0的星期日期值。
有兩種方法可以訂正這個問題。你可以調(diào)用Date.date這一名字極易讓人混淆的方法,它返回的是月份日期。然而,與大多數(shù)Date方法一樣,它已經(jīng)被棄用了,因此你是將Date徹底拋棄,直接調(diào)用Calendar的get(Calendar.DAY_OF_MONTH)方法。用這兩種方法,該程序都可以打印出我們想要的1999 31:
public class DatingGame {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(1999, Calendar.DECEMBER, 31);
System.out.print(cal.get(Calendar.YEAR) + " ");
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
}
}
本謎題只是掀開了Calendar和Date缺陷的冰山一角。這些API簡直就是雷區(qū)。Calendar其他的嚴(yán)重問題包括弱類型(幾乎每樣事物都是一個int)、過于復(fù)雜的狀態(tài)空間、拙劣的結(jié)構(gòu)、不一致的命名以及不一致的雨衣等。在使用Calendar和Date的時候一定要當(dāng)心,千萬要記著查閱API文檔。
對API設(shè)計者來說,其教訓(xùn)是:如果你不能在第一次設(shè)計時就使它正確,那么至少應(yīng)該在第二次設(shè)計時應(yīng)該使它正確,絕對不能留到第三次設(shè)計時去處理。如果你對某個API的首次嘗試出現(xiàn)了嚴(yán)重問題,那么你的客戶可能會原諒你,并且會再給你一次機(jī)會。如果你第二次嘗試又有問題,你可能會永遠(yuǎn)堅持這些錯誤了。