Calendar.getInstance()看起来应该是个单例,但实际上并不是。
一次在JProfile中查看CPU的消耗的时候,发现 Calendar.getInstance() 消耗的CPU占比比较大,具体看了下代码才发现实际上是每次都创建对象的。
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
代码中可以清楚的看到是每次都创建的。
那么创建对象的成本怎么样呢?
public static void main(String[] args) {
int runs = 10000;
long start = System.nanoTime();
Calendar cal = null;
for(int i=0;i<runs;i++)
cal = Calendar.getInstance();
long time = System.nanoTime() - start;
System.out.println("Calendar.getInstance() took on average "+time/runs+" ns. "+cal);
long start2 = System.nanoTime();
long now = 0;
for(int i=0;i<runs;i++)
now = System.currentTimeMillis();
long time2 = System.nanoTime() - start2;
System.out.println("System.currentTimeMillis() took on average "+time2/runs+" ns. "+now);
}
测试结果:
Calendar.getInstance() took on average 8264 ns. java.util.GregorianCalendar[time=1481014104709,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Chongqing",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=11,WEEK_OF_YEAR=50,WEEK_OF_MONTH=2,DAY_OF_MONTH=6,DAY_OF_YEAR=341,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=48,SECOND=24,MILLISECOND=709,ZONE_OFFSET=28800000,DST_OFFSET=0]
System.currentTimeMillis() took on average 105 ns. 1481014104711
可以看到是System.currentTimeMillis()的80多倍。性能其实是很差了的。
建议Calendar对象可以缓存起来,不用每次都创建。
具体的讨论在stackoverflow上也有: http://stackoverflow.com/questions/4587878/creating-java-object-general-question