查看 String 的源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
/**
* The value is used for character storage.
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*
* Additionally, it is marked with {@link Stable} to trust the contents
* of the array. No other facility in JDK provides this functionality (yet).
* {@link Stable} is safe here, because value is never null.
*/
@Stable
private final byte[] value;
/**
* The identifier of the encoding used to encode the bytes in
* {@code value}. The supported values in this implementation are
*
* LATIN1
* UTF16
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*/
private final byte coder;
/** Cache the hash code for the string */
private int hash; // Default to 0
/**
* Cache if the hash has been calculated as actually being zero, enabling
* us to avoid recalculating this.
*/
private boolean hashIsZero; // Default to false;
...其中 value 被 final 修饰,不可指向其它对象。
为什么要设计成不可变?
线程安全。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
支持
hash映射和缓存。因为String的hash值经常会使用到,比如作为Map的键,不可变的特性使得hash值也不会变,不需要重新计算。出于安全考虑。
URL,path,password通常情况下都是以String类型保存,假若String不是固定不变的,将会引起各种安全隐患。字符串常量池优化。
String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
调用 substring() 等 API 时,本质上是创建了一个新对象然后赋值。
Tips: 为什么自 JDK 9 之后
value的类型由char[]变为了byte[]?主要是为了节约
String占用的内存。在大部分 Java 程序的堆内存中,
String占用的空间最大,并且绝大多数String只有Latin-1字符,这些Latin-1字符只需要 1 个字节就够了。而在 JDK 9 之前,JVM 因为
String使用char数组存储,每个char占 2 个字节,所以即使字符串只需要 1 字节,它也要按照 2 字节进行分配,浪费了一半的内存空间。到了 JDK 9 之后,对于每个字符串,会先判断它是不是只有
Latin-1字符,如果是,就按照 1 字节的规格进行分配内存,如果不是,就按照 2 字节的规格进行分配,这样便提高了内存使用率,同时 GC 次数也会减少,提升效率。不过
Latin-1编码集支持的字符有限,比如不支持中文字符,因此对于中文字符串,用的是 UTF-16 编码(两个字节),所以用byte[]和char[]实现没什么区别。