Skip to content

Latest commit

 

History

History
54 lines (36 loc) · 2.88 KB

为什么 String 被设计为不可变的.md

File metadata and controls

54 lines (36 loc) · 2.88 KB

为什么 String 被设计为不可变的

字符串常量池

String 不可变的第一个好处是可以使用字符串常量池。在 Java 中有字符串常量池的概念,比如两个字符串变量的内容一样,那么就会指向同一个对象,而不需创建第二个同样内容的新对象,例如:

String s1 = "lagou";
String s2 = "lagou";

其实 s1 和 s2 背后指向的都是常量池中的同一个“lagou”。正是因为这样的机制,再加上 String 在程序中的应用是如此广泛,我们就可以节省大量的内存空间。

用作 HashMap 的 key

String 不可变的第二个好处就是它可以很方便地用作 HashMap (或者 HashSet) 的 key。通常建议把不可变对象作为 HashMap的 key,比如 String 就很合适作为 HashMap 的 key。

对于 key 来说,最重要的要求就是它是不可变的,这样我们才能利用它去检索存储在 HashMap 里面的 value。由于 HashMap 的工作原理是 Hash,也就是散列,所以需要对象始终拥有相同的 Hash 值才能正常运行。如果 String 是可变的,这会带来很大的风险,因为一旦 String 对象里面的内容变了,那么 Hash 码自然就应该跟着变了,若再用这个 key 去查找的话,就找不回之前那个 value 了。

缓存 HashCode

String 不可变的好处就是缓存 HashCode。

在 Java 中经常会用到字符串的 HashCode,在 String 类中有一个 hash 属性,代码如下:

/** Cache the hash code for the String */
private int hash;

String 类中重新了 hashCode 方法:

    public int hashCode() {
        int h = hash;
        final int len = length();
        if (h == 0 && len > 0) {
            for (int i = 0; i < len; i++) {
                h = 31 * h + charAt(i);
            }
            hash = h;
        }
        return h;
    }

这是一个成员变量,保存的是 String 对象的 HashCode。因为 String 是不可变的,所以对象一旦被创建之后,HashCode 的值也就不可能变化了,我们就可以把 HashCode 缓存起来。这样的话,以后每次想要用到 HashCode 的时候,不需要重新计算,直接返回缓存过的 hash 的值就可以了,因为它不会变,这样可以提高效率,所以这就使得字符串非常适合用作 HashMap 的 key。

而对于其他的不具备不变性的普通类的对象而言,如果想要去获取它的 HashCode ,就必须每次都重新算一遍,相比之下,效率就低了。

线程安全

String 不可变的还有一个好处是线程安全,因为具备不变性的对象一定是线程安全的,我们不需要对其采取任何额外的措施,就可以天然保证线程安全。由于 String 是不可变的,所以它就可以非常安全地被多个线程所共享,这对于多线程编程而言非常重要,避免了很多不必要的同步操作。