SimpleDateFormat中字符串常量生成问题

场景描述

在String练习中遇到以下问题,注释掉第一行输出true,加上第一行输出false,代码如下:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 

String s3 = new String("1") + new String("1"); 
s3.intern(); 
String s4 = "11"; 
System.out.println(s3 == s4);

问题分析

intern方法在jdk7及以上的特性:

  • 当常量池中不存在"11"这个字符串的引用,将对象s3的引用加入常量池,返回对象s3的引用
  • 当常量池中存在"11"这个字符串的引用,返回常量池中的引用

先不看第一行,逐行分析以上代码

  • String s3 = new String("1") + new String("1");:会生成两个匿名对象和对象s3,同时在字符串常量池放入"1"。
  • s3.intern():由于字符串常量池中找不到"11",把对象s3的引用放入字符串常量池中
  • String s4 = "11":由于字符串常量池中已存在"11",此时s4指向对象s3的引用
  • System.out.println(s3 == s4):s3、s4指向同一个引用,因此输出true

但是加上第一行,输出就变成false了,为什么?
猜测:SimpleDateFormat创建时向常量池中添加了"11",因为是和时间相关,改成"10"、"12"都输出false,"13"又变成true,基本可以验证我们的猜想。接下来看看在什么时候向常量池中添加了"11"

验证

快速定位方法,执行以下代码,简化debug追踪路径:

LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DateFormatSymbolsProvider.class, locale); 
ResourceBundle resource = ((ResourceBundleBasedAdapter)adapter).getLocaleData().getDateFormatData(locale); resource.getStringArray("MonthNames"); 

String s3 = new String("1") + new String("1"); 
s3.intern(); 
String s4 = "11";
System.out.println(s3 == s4);

完整debug追踪路径如下:

  • SimpleDateFormat初始化时需要调用:DateFormatSymbols.getInstanceRef(locale)

  • DateFormatSymbols类中接着调用getProviderInstance(locale)实例化DateFormatSymbols对象

  • 通过一个Provider生成DateFormatSymbols实例

  • provider的实现类DateFormatSymbolsProviderImpl直接调用其构造函数

  • 构造函数中进行初始化:initializeData(locale)

  • initializeData中构建ResourceBundle

  • ResourceBundle构建好后,获取months数组,调用:resource.getStringArray("MonthNames")

  • ResourceBundle中调用getObject(key)

  • 首次执行obj==null,会调用parent.getObject(key)

  • handleGetObject是一个抽象方法,实际调用的是子类ParallelListResourceBundle中的实现方法

  • 在loadLookupTablesIfNecessary中会调用this.getContents()

  • var2是一个二维数组,看下标4的位置1~12的字符串

  • 接下来我们看下this.getContents(),这也是个抽象方法,定位到其实现类FormatData

  • 下面是getContents()的返回内容,大量的字符串

{
	{
		"MonthNames",
		new String\[\] {
			"January",
			"February",
			"March",
			"April",
			"May",
			"June",
			"July",
			"August",
			"September",
			"October",
			"November",
			"December",
			""
		}
	}, {
		"MonthAbbreviations",
		new String\[\] {
			"Jan",
			"Feb",
			"Mar",
			"Apr",
			"May",
			"Jun",
			"Jul",
			"Aug",
			"Sep",
			"Oct",
			"Nov",
			"Dec",
			""
		}
	}, {
		"MonthNarrows",
		new String\[\] {
			"1",
			"2",
			"3",
			"4",
			"5",
			"6",
			"7",
			"8",
			"9",
			"10",
			"11",
			"12",
			""
		}
	}, {
		"DayNames",
		new String\[\] {
			"Sunday",
			"Monday",
			"Tuesday",
			"Wednesday",
			"Thursday",
			"Friday",
			"Saturday"
		}
	}, {
		"DayAbbreviations",
		new String\[\] {
			"Sun",
			"Mon",
			"Tue",
			"Wed",
			"Thu",
			"Fri",
			"Sat"
		}
	}, {
		"DayNarrows",
		new String\[\] {
			"S",
			"M",
			"T",
			"W",
			"T",
			"F",
			"S"
		}
	}, {
		"AmPmMarkers",
		new String\[\] {
			"AM",
			"PM"
		}
	}, {
		"narrow.AmPmMarkers",
		new String\[\] {
			"a",
			"p"
		}
	}, {
		"Eras",
		var1
	}, {
		"short.Eras",
		var1
	}, {
		"narrow.Eras",
		new String\[\] {
			"B",
			"A"
		}
	}, {
		"buddhist.Eras",
		var2
	}, {
		"buddhist.short.Eras",
		var2
	}, {
		"buddhist.narrow.Eras",
		var2
	}, {
		"japanese.Eras",
		var4
	}, {
		"japanese.short.Eras",
		var3
	}, {
		"japanese.narrow.Eras",
		var3
	}, {
		"japanese.FirstYear",
		new String\[0\]
	}, {
		"NumberPatterns",
		new String\[\] {
			"#,##0.###;-#,##0.###",
			"¤ #,##0.00;-¤ #,##0.00",
			"#,##0%"
		}
	}, {
		"DefaultNumberingSystem",
		""
	}, {
		"NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"0",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"�"
		}
	}, {
		"arab.NumberElements",
		new String\[\] {
			"٫",
			"٬",
			"؛",
			"٪",
			"٠",
			"#",
			"-",
			"اس",
			"؉",
			"∞",
			"NaN"
		}
	}, {
		"arabext.NumberElements",
		new String\[\] {
			"٫",
			"٬",
			"؛",
			"٪",
			"۰",
			"#",
			"-",
			"×۱۰^",
			"؉",
			"∞",
			"NaN"
		}
	}, {
		"bali.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᭐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"beng.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"০",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"cham.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"꩐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"deva.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"०",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"fullwide.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"0",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"gujr.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"૦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"guru.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"੦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"java.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"꧐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"kali.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"꤀",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"khmr.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"០",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"knda.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"೦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"laoo.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"໐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"lana.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᪀",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"lanatham.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᪐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"latn.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"0",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"�"
		}
	}, {
		"lepc.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᱀",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"limb.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᥆",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"mlym.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"൦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"mong.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᠐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"mtei.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"꯰",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"mymr.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"၀",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"mymrshan.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"႐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"nkoo.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"߀",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"olck.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᱐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"orya.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"୦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"saur.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"꣐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"sund.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᮰",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"talu.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"᧐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"tamldec.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"௦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"telu.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"౦",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"thai.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"๐",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"�"
		}
	}, {
		"tibt.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"༠",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"vaii.NumberElements",
		new String\[\] {
			".",
			",",
			";",
			"%",
			"꘠",
			"#",
			"-",
			"E",
			"‰",
			"∞",
			"NaN"
		}
	}, {
		"TimePatterns",
		new String\[\] {
			"h:mm:ss a z",
			"h:mm:ss a z",
			"h:mm:ss a",
			"h:mm a"
		}
	}, {
		"DatePatterns",
		new String\[\] {
			"EEEE, MMMM d, yyyy",
			"MMMM d, yyyy",
			"MMM d, yyyy",
			"M/d/yy"
		}
	}, {
		"DateTimePatterns",
		new String\[\] {
			"{1} {0}"
		}
	}, {
		"buddhist.TimePatterns",
		new String\[\] {
			"H:mm:ss z",
			"H:mm:ss z",
			"H:mm:ss",
			"H:mm"
		}
	}, {
		"buddhist.DatePatterns",
		new String\[\] {
			"EEEE d MMMM G yyyy",
			"d MMMM yyyy",
			"d MMM yyyy",
			"d/M/yyyy"
		}
	}, {
		"buddhist.DateTimePatterns",
		new String\[\] {
			"{1}, {0}"
		}
	}, {
		"japanese.TimePatterns",
		new String\[\] {
			"h:mm:ss a z",
			"h:mm:ss a z",
			"h:mm:ss a",
			"h:mm a"
		}
	}, {
		"japanese.DatePatterns",
		new String\[\] {
			"GGGG yyyy MMMM d (EEEE)",
			"GGGG yyyy MMMM d",
			"GGGG yyyy MMM d",
			"Gy.MM.dd"
		}
	}, {
		"japanese.DateTimePatterns",
		new String\[\] {
			"{1} {0}"
		}
	}, {
		"DateTimePatternChars",
		"GyMdkHmsSEDFwWahKzZ"
	}, {
		"calendarname.islamic-umalqura",
		"Islamic Umm al-Qura Calendar"
	}
}

使用方式

由上可知,创建一个SimpleDateFormat对象会加载挺多内容。那么,使用时能否只创建一个实例,使用单例模式呢?
答案是否,因为SimpleDateFormat 是非线程安全的,正确的打开方式如下:

  • 使用ThreadLocal
public static ThreadLocal<DateFormat> safeSdf = new ThreadLocal<DateFormat>() {
    @Override 
    protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }
}
  • DateTimeFormatter
// 解析日期
String dateStr= "2020-07-16";
String dateStr = "2020-07-16";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(dateStr, formatter);
System.out.println(date);

// 日期转换为字符串
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
String nowStr = now.format(format);
System.out.println(nowStr);

posted @ 2021-06-16 10:55  肆玖爺  阅读(121)  评论(0)    收藏  举报