-
简介
String对象(线程安全,可查看源码)又称为不可变字符串,即不能修改字符串中的字符。其优点:编译器可以让字符串共享。
Java设计者认为共享带来的高效率远胜于提取、拼接字符串所带来的低效率(字符串更多的是进行比较)。
Java设计者认为共享带来的高效率远胜于提取、拼接字符串所带来的低效率(字符串更多的是进行比较)。
每个用双括号括起来的字符串(称为字符串常量),总是指向字符串池中的一个对象
-
存储
由于字符串对象的大量使用(它是一个对象,一般而言对象总是在堆中分配内存),为了节省内存空间和运行时间,在编译阶段就把所有的字符串文字放到字符串常量池中,该池中所有相同的字符串常量被合并,只占用一个空间。
- 创建
1)字面量直接定义
如:String str=“seraph”
如:String str=“seraph”
JVM在字符串常量池中寻找内容为seraph的对象:
若找不到则创建这个对象,然后将创建的对象的引用放入字符串常量池,并且将引用返回给变量str。
若找到,则将已存在的字符串对象的引用返回给str。
若找不到则创建这个对象,然后将创建的对象的引用放入字符串常量池,并且将引用返回给变量str。
若找到,则将已存在的字符串对象的引用返回给str。
2)new关键字标准构造
如:String str=new String(“seraph”)
如:String str=new String(“seraph”)
new创建字符串时首先查看池中是否有相同值的字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址;
如果池中没有,则在堆中创建一份,然后返回堆中的地址(注意,此时不需要从堆中复制到池中,否则,将使得堆中的字符串永远是池中的子集,导致浪费池的空间)
如果池中没有,则在堆中创建一份,然后返回堆中的地址(注意,此时不需要从堆中复制到池中,否则,将使得堆中的字符串永远是池中的子集,导致浪费池的空间)
如果想将这个对象的引用放入字符串常量池,则使用intern方法,调用intern后,首先检查字符串常量池中是否有该对象的引用,如果存在,则将其返回给变量,否则将引用加入并返回给变量。
3)串联生成
常量表达式(constant expression),即使用+连接常量字符串。
如String s = “a” + "b" + "c"; 编译器将在编译时优化为String s = “abc”;
如果右操作数含有一个或一个以上的字符串引用时,则在运行时堆中再建立一个字符串对象,返回引用。
如String s=str+ “ous”;
如String s=str+ “ous”;
测试:
String a = "abc";
String b = "abc";
String c = "a" + "b" + "c";
String d = new String("abc");
String e = new String("abc".getBytes());
String f = e.intern();
String temp = "c";
String g = "ab" + temp;
System.out.println(a == b);//true
System.out.println(b == c);//true
System.out.println(d == a);//false
System.out.println(e == a);//false
System.out.println(f == a);//true
System.out.println(g == a);//false
字符串引用关系总结:
1)字符串字面量不管是否在相同包/相同类都引用相同对象。
2)通过常量表达式(constant expression)计算的字符串在编译时被计算并被视为字符串字面量
3)运行时通过串联计算的字符串是新创建的,因此引用不同。
4)显式的调用计算后的String对象的intern方法和之前相同内容的任意已经存在的字符串字面量是相同的对象。
- hashCode
hashCode中存在良性数据竞争,即hashCode是延迟初始化的,但是因为初始化多少次都无所谓,所以数据竞争是良性的。
参考:
《JLS8》3.10.5 String Literals
转载请注明出处!