概述
在Java8中,String内部使用char数组存储数据。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
在Java9之后,String类的实现改用byte数组存储字符串,同时使用coder
来标识使用了哪种编码。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final byte[] value;
/** The identifier of the encoding used to encode the bytes in {@code value}. */
private final byte coder;
}
value数组被声明为final,并且String没有提供可以更改value数组的方法,所以String不可以变。
String 中的 +
String s = "123" + "456";
等价于
String s = "123456";
String s1 = "123";
String s2 = "456";
String s3 = s1 + s2;
等价于
String s3 = new StringBuilder(s1).append(s2).toString();
final String s1 = "123";
final String s2 = "456";
String s3 = s1 + s2;
等价于
String s3 = "123456";
String、StringBuffer、StringBuilder
- 可变性
- String 不可变
- StringBuffer和StringBuilder可变
- 线程安全
- String不可变,因此是线程安全的
- StringBuilder不是线程安全的
- StringBuffer是线程安全的(每个方法都用synchronized加锁)
字符串常量池(String Pool)
字符串常量池保存着所有字符串字面量,也可以通过String的intern()
方法在运行过程中将字符串添加到String Pool中。
当一个字符串调用intern()
方法时,如果String Pool中已经存在一个字符串和该字符串值相等(equal()
),那么就会返回String Pool中字符串中的引用;否则,就会在String Pool中添加一个新的字符串,并返回这个新字符串的引用。
String s1 = "123"; // 指向常量池的引用
String s2 = new String("123"); // 指向堆上的引用
String s3 = s2.intern();
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
JDK1.6中,常量池在永久代,intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用。
JDK1.7中,常量池移到Java堆中,intern方法不会再复制实例,而是直接存储堆中的引用
new String(“123”)
使用这种方式一共创建两个字符串对象(前提是String Pool中还没有“123”字符串对象)
- 常量池中
- 堆中
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
从String的构造方法中可以看到,value数组是共享的,hash也是共享的。
参考