前言
在 JAVA 语言中有8中基本类型和一种比较特殊的类型String
。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。
String
类型的常量池主要使用方法有以下两种(基于JDK7以后的版本)
-
直接使用双引号声明的
String
对象会直接存储在常量池中。例如:String str="abc";
该语句对在创建常量池中创建一个"abc"字符串对象。 -
如果不是用双引号声明的
String
对象,可以使用String
提供的intern
方法。intern
方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。例如:
/*
执行该语句会在字符串常量池中创建对象"abc"和"123",已及JAVA Heap中str引用指向的对象
(还有两个匿名对象new String("abc")、new String("123"),先不做讨论)。
此时str引用的对象内容为"abc123",但此时常量池中没有"abc123"这个对象。
*/
String str=new String("abc")+String("123");
/*执行str.interm();语句是将str中的"abc123"字符串放入 String 常量池中,因为此时常量池中不存在“abc123”字符串*/
str.interm();
通过两段代码进一步理解interm()方法
/*代码段1*/
public static void main(String[] args) {
String s1 = new String("a");
s1.intern();
String s2 = "a";
System.out.println(s1 == s2);
String s3 = new String("b") + new String("b");
s3.intern();
String s4 = "bb";
System.out.println(s3 == s4);
}
输出结果为:
false
true
- 我们看语句
String s1 = new String("a");
,生成了常量池中创建"a"和Java Heap中的字符串对象,s1.intern();
这一句是 s1 对象去常量池中寻找后发现 “a” 已经在常量池里了。 - 接下来
String s2 = "a";
这句代码是生成一个 s2的引用直接指向常量池中的“a”对象。 结果就是 s 和 s2 的引用地址明显不同。所以输出false
。 - s3和s4字符串。
String s3 = new String("b") + new String("b");
,这句代码中现在生成了2最终个对象,是字符串常量池中的“b” 和 JAVA Heap 中的 s3引用指向的对象。中间还有2个匿名的new String("b")
我们不去讨论。此时s3引用对象内容是”bb”,但此时常量池中是没有 “bb”对象的。 - 接下来
s3.intern();
这一句代码,是将 s3中的“bb”字符串放入 String 常量池中,因为此时常量池中不存在“bb”字符串,所以在常量池中生成一个 “bb” 的对象,关键点是常量池也在 Java Heap区域,常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。因此输出true
。
/*代码段2*/
public static void main(String[] args) {
String s1 = new String("a");
String s2 = "a";
s1.intern();
System.out.println(s1 == s2);
String s3 = new String("b") + new String("b");
String s4 = "bb";
s3.intern();
System.out.println(s3 == s4);
}
false
false
- s 1和 s2 代码中,
s1.intern();
,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s1= new String("a");
的时候已经生成“a”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s1 和 s2 的引用地址是不会相等的。 - 执行
String s4 = "bb";
声明 s4 的时候常量池中是不存在“bb”对象的,执行完毕后,“bb“对象是 s4 声明产生的新对象。然后再执行s3.intern();
时,常量池中“bb”对象已经存在了,因此 s3 和 s4 的引用是不同的。所以俩比较均输出fales
。
总结
在JDK7后String.intern() 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象。