为什么在Java中String是不可变的
1.简介
在Java中,字符串是不可变的。在访谈中一个非常普遍的明显问题是“为什么在Java中将字符串设计为不可变的?”
Java的创建者詹姆斯·高斯林(James Gosling )曾在一次采访中被问及 何时应该使用不可变对象,他回答了: 我会尽可能使用不可变的。 他进一步支持其论点,指出了不变性提供的功能,例如缓存,安全性,无需复制即可轻松重用等。
2.什么是不可变对象?
不变对象是在完全创建后其内部状态保持不变的 对象。这意味着一旦将对象分配给变量,我们将无法通过任何方式更新引用或更改内部状态。
3.为什么字符串在Java中是不可变的?
保持此类不变的主要好处是缓存,安全性,同步和性能。
让我们讨论一下这些事情是如何工作的。
3.1 字符串池
字符串是最广泛使用的数据结构。缓存String文字并重新使用它们可节省大量堆空间,因为不同的String变量引用String池中的同一对象。字符串池正是用于此目的。
Java字符串池是 JVM存储字符串的特殊内存区域 。由于字符串在Java中是不可变的,因此JVM通过在池中仅存储每个文字字符串的一个副本来优化为其分配的内存量。
3.2 安全
字符串被广泛使用的Java应用程序中存储的敏感信息,像条用户名,密码,连接的URL,网络连接等,它也是由JVM类加载器在加载类广泛使用。
因此,一般而言,保护String类对于整个应用程序的安全性至关重要。例如,考虑以下简单代码段:
在上面的代码片段中,假设我们从一个不可信的来源接收到一个String对象。 最初,我们将进行所有必要的安全检查,以检查String是否仅是字母数字,然后进行一些其他操作。
请记住,我们不可靠的源调用方方法仍然引用了此userName对象。
如果字符串是可变的,那么在执行更新时,即使执行安全检查后,我们也无法确定收到的字符串是否安全。 不可信的调用方方法仍然具有引用,并且可以在完整性检查之间更改String。 因此,在这种情况下,我们的查询易于进行SQL注入。因此,可变的字符串可能会导致安全性随时间下降。
也可能发生字符串 userName 对另一个线程可见的情况,然后在完整性检查后可以更改其值。
通常,在这种情况下,不变性可以帮助我们解决问题,因为在值不变的情况下使用敏感代码更容易操作,因为可能影响结果的操作交错较少。
3.3 同步化
自动不可变使String线程安全,因为从多个线程访问它们时不会更改它们。
因此,不可变对象通常可以在同时运行的多个线程之间共享。它们也是线程安全的,因为如果线程更改了该值,则将在字符串池中创建一个新的字符串, 而不是对其进行修改。因此,字符串对于多线程是安全的。
3.4哈希码缓存
由于String对象大量用作数据结构,因此它们也广泛用于HashMap,HashTable,HashSet等哈希实现中。 在对这些哈希实现进行操作时,hashCode()方法被频繁调用以进行存储。
不变性确保Strings的值不变。因此,在String类中重写hashCode()方法以方便缓存, 从而在第一次hashCode()调用期间计算并缓存了哈希,此后返回了相同的值。
反过来,这可以改善在对String对象进行操作时使用哈希实现的集合的性能。
在另一方面,可变字符串将产生在插入和取回的两个不同时间散列码如果内容的字符串被操作之后修改,可能会丢失在值对象地图
3.5 性能
如我们先前所见,字符串池之所以存在是因为字符串是不可变的。反过来,通过使用字符串操作时,它可以节省堆内存并更快地访问哈希实现,从而提高性能。
由于String是使用最广泛的数据结构,因此,改善String的性能通常会对改善整个应用程序的性能产生重大影响。
Last updated