要理解清楚java String,首选需要区分String Literals和String类
一. 字符串字面值(String Literals)
- 一个String Literal 由0个到多个字符组成,并且包在双引号中间。
- 一个String Literal 总是String 类型的。
- 一个String Literal 是String类的实例的引用。
- String Literal总是指向同一个String实例,这是因为String Literal是常量表达式,并且被”interned”了,这样就可以共享String实例提高效率了。
二. String类
- String实例代表了Unicode编码的字符序列
- String Literal 是String类实例的引用
三. 连接操作
- String的连接(+)将产生一个新的String实例,除非这个表达式是常量表达式。
四. 实例
//两个包
package other;
public class Other { public static String hello = "Hello"; }
package testPackage;
class Test {
public static void main(String[] args) {
String hello = "Hello", lo = "lo";
System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
System.out.print((hello == ("Hel"+"lo")) + " ");
System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());
} }
class Other { static String hello = "Hello"; }
执行结果
true true true true false true
结论
1. Listeral Strings在同一个Class同一个package下,是引用了相同的String对象。
2. Listeral Strings在同一个package不同的Class下,是引用了相同的String对象。
3. Listeral Strings在不同的package不同的Class下,也是引用了相同的String对象。
4. String的常量表达式计算是在编译期间发生的,并且作为Listeral对待。
5. Strings的连接计算是在运行时发生的,会创造 一个新的String对象,因此是不同的。
6. intern计算得到的结果和之前存在的Listeral是同一个String对象。
四. 分配与回收
- String Listeral指向的String实例是分配在堆的特殊区域中的。
- String Listeral指向的String实例是不会被回收的。我们可以不停的调用String.intern,然后就会报内存溢出的异常。
- 对象个数,经常有面试的问,下面的代码分别了几个对象。有了上面的知识,就比较轻松的知道应该是两个对象。其实分别了几个对象,只要写个小李子,用==判断一下就可以了。
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
六. intern的使用
一般情况下,我们不会用到intern方法。但是,有时intern确实可以节省大量的内存和CPU。比如我们的应用需要保存住在Greece的所有人的详细住址信息,我们用下面的结构来保存。
class Address{
private String city;
private String street;
private String streetNumber;
private String zipCode;
}
假如Athens市中住了400万人,那city就需要保存400万个Athens值。如果我们使用了city.intern()的话,就会节省大量的空间。
但是使用时必须小心,在java7之前,String pool是保存在堆中的特殊区域,称之为”PermGen”,PermGen是固定大小的,只能保存一定数量的String Listeral,所以只有非常熟练的人才去使用intern。 但是从java7开始,这个区域将会保存在普通的堆区,和其它的对象一样。是已hashmap的形式存在的,并且你可以用-XX:StringTableSize来调整大小。
七. String Pool大小
要注意的是,String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找)。
在 jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。在jdk7中,StringTable的长度可以通过一个参数指定:
-XX:StringTableSize=99991
具体的intern详解,请查看深入解析String#intern