String字符串是java的一种重要数据类型,从String源码中可以看出String是对char数组的一种封装和延伸,主要组成部分是char数组、offset偏移量、count长度。String的真实内容由偏移量和长度在char数组中定位和截取。
本文章中将会讲到String的不变性和常量池
1、String对象具有不变性,对象一旦生成就不能再改变。源码中是这样说明的:Strings are constant; their values cannot be changed after they are created。
为什么String对象生成后就不能改变的呢?
这个是由于(1)String类是由final关键字修饰的,这样可以保证它不可能含有子类。(2)它的所有属性也是由final修饰,确保了只能在对象被构造时被赋值一次。这也确保了不能给此属性提供setter方法。
这样做的好处: String的不变性可以称之为不变模式(即一个对象的状态在创建后就不会再发生改变),这样做的好处保证其不可能有任何子类,对系统起到了保护作用。同时由于它的不变性可以在多线程下被共享,不需要同步机制或者锁机制,从而提高了系统性能。
部分源码截图(jdk8)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
2、对于字符串的常量池是String的一大特点,但是又很容易在此产生误解
字符串的针对常量池做了一些优化,可以理解为: 只要字符串内容相同,最终都会指向同一个常量池。当一个字符串反复出现的时候,可以大大节省内存空间。
针对下面例子详细解释:
public class StringTest {
public static void main(String[] args){
String str = new String("a");
String str1 = "a";
String str2 = "a";
System.out.println(str==str2);
System.out.println(str1==str2);
System.out.println(str1==str.intern());
}
}
结果:
从结果看出: str1==str2 返回true 说明str1和str2引用了相同的地址,str==str2 返回false 说明了str重新开辟了内存空间。但是str1==str.intern() 返回结果为true。可见str1和str指向了同一常量池。
tip: intern() 可以返回在String对象在常量池中的引用。源码中解释:a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.
咱们在分析一下常量池内容(在cmd中输入 javap -v StringTest.class)
图1:
图2:
由图2中用框圈住的可以看出,这三个变量都指向了常量池中的的#3,也就是图一用红框圈住的。
String的内存分配方式如下:
总之而言: 只要字符串内容相同,最终都会指向同一个常量池地址。
参考文章:
1. 《Java程序性能优化 让你的Java程序更快、更稳定》. 葛一鸣
2. JDK8 源码