源码分析
首先我们先看一下String类的源码
public final class String implements Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
private final byte coder;
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
static final boolean COMPACT_STRINGS = true;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new String.CaseInsensitiveComparator();
static final byte LATIN1 = 0;
static final byte UTF16 = 1;
首先,我们可以看到String是一个被final修饰的类,这就限制了它不能被继承,无法在其他类中修改String类 中的属性值,然后我们可以看到用来存储字符串内容的是一个value数组,value被private修饰,成为私有的,只有String类本身可以修改和访问,然而同时value数组也被final修饰,在value初始化完成后不能被其他数组引 用,在String类方法中也没有改变数组元素值的方法,所以不管是在其他类中,还是在String本身这个类中都无法修改数组的引用,和数组的值,这就是String字符串为什么不可变的原因。
String字符串在内存中的存储
字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中。
String字符串在内存中的存储有两种方式,一种是在字符串常量池(方法区)里面,另一种是在堆中的对象中;
我们使用String s = “hahaha”的时候,“hahaha” 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 “hahaha” 字符串字面量;,如果之前已经创建过“hahaha”这个对象了或者说常量池里面已经有这个对象了,那就会直接把之前创建的对象引用直接赋值给s,不会重复创建多个。
当我们使用 new String(“abc”) 这种方式会比之前多创建一个对象,不仅在堆中创建了一个字面常量对象,还在堆中创建了一个字符串对象使用
总结:就是在字符串常量池中创建对象的时候,是唯一的,不会重复创建多个,但是在堆中的对象里创建字符串对象是的时候没有这个限制,可以创建多个。
String字符串可变的假象
public class StringTest {
public static void main(String[] args) {
String s = new String("hahaha");
s = "hehehe";
String s1 = "hahaha";
System.out.println(s);
//hehehe
}
}
输出的结果是hehehe,这不就证明了String是可变的吗?,其实不然,我们先看一下两个字符串的地址。
再看一下字符串在内存中的状态
通过上边的观察我们可以发现两个字符串"hahaha"和"hahaha"的地址并不一样,说明什么?说明这两个字符串根本就不是同一个字符串对象,那么这个例子就不能推翻String是不可变的那么为什么会出现这种情况呢?其实当我们在试图修改一个字符串的值的时候jvm会自动给我们重新生成一个字符串对象,并且把需要修改字符串对象的句柄指向新创建好的对象。字符串不可变指的是字符串对象不可变,并不是字符串对象的引用不可变。 所以String字符串是不可变的没错。
不可变的好处
-
可以缓存 hash 值
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
-
String Pool 的需要
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
-
安全性
String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 的那一方以为现在连接的是其它主机,而实际情况却不一定是。
-
线程安全
String 不可变性天生具备线程安全,可以在多个线程中安全地使用。
来源:CSDN
作者:辉大咖
链接:https://blog.csdn.net/weixin_45804960/article/details/103531648