Java中的String字符串不可变

源码分析

首先我们先看一下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字符串在内存中的存储有两种方式,一种是在字符串常量池(方法区)里面,另一种是在堆中的对象中;
Java中的String字符串不可变_第1张图片
我们使用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是可变的吗?,其实不然,我们先看一下两个字符串的地址。
在这里插入图片描述
在这里插入图片描述
再看一下字符串在内存中的状态
Java中的String字符串不可变_第2张图片
通过上边的观察我们可以发现两个字符串"hahaha"和"hahaha"的地址并不一样,说明什么?说明这两个字符串根本就不是同一个字符串对象,那么这个例子就不能推翻String是不可变的那么为什么会出现这种情况呢?其实当我们在试图修改一个字符串的值的时候jvm会自动给我们重新生成一个字符串对象,并且把需要修改字符串对象的句柄指向新创建好的对象。字符串不可变指的是字符串对象不可变,并不是字符串对象的引用不可变。 所以String字符串是不可变的没错。

不可变的好处

  1. 可以缓存 hash 值

    因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

  2. String Pool 的需要

    如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

  3. 安全性

    String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 的那一方以为现在连接的是其它主机,而实际情况却不一定是。

  4. 线程安全

String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

你可能感兴趣的:(java,字符串)