我们不能确定Java设计师在设计时实际上在想什么 String
但是只能基于从字符串不变性中获得的优势来总结这些原因,其中一些是
1.字符串常量池的存在
如“ 为什么字符串存储在字符串常量池中”中所述文中讨论的那样,每个应用程序都会创建太多的字符串对象,并且为了避免JVM首先创建大量的字符串对象,然后再进行垃圾回收,从而节省了JVM。JVM将所有字符串对象存储在称为字符串常量池的单独内存区域中,并重用该缓存池中的对象。
每当我们创建字符串文字时,JVM都会首先查看该文字是否已存在于常量池中,如果常量池中已经存在,新引用将开始指向SCP中的同一对象。
String a = "Naresh";
String b = "Naresh";
String c = "Naresh";
在与值上面的例子中的字符串对象Naresh
将在SCP获得创建只有一次,所有参考a
,b
,c
会如果我们试图在改变指向同一个对象,但什么a
如a.replace("a", "")
。
理想情况下,a
应该有值Nresh
,但是b
,c
应该维持不变,因为我们一直在变动中的最终用户a
只。而我们知道a
,b
,c
所有都指向同一个对象,因此,如果我们做出改变a
,其他人也应反映这一变化。
但是字符串的不变性使我们避免了这种情况,并且由于字符串对象的不变性,字符串对象Naresh
永远不会改变。因此,当我们进行任何更改a
而不是更改字符串对象时,Naresh
JVM都会创建一个新对象,将其分配给该对象a
,然后在该对象中进行更改。
因此,仅由于String的不可变性,才可能使用String池,并且如果String不能保持不变,则缓存字符串对象并重新使用它们是不可能的,因为任何变量都会更改值并破坏其他变量。
这就是为什么它由JVM非常专门地处理并被赋予一个特殊的内存区域的原因。
2.线程安全
当一个对象在多个线程上运行时,该对象被称为线程安全的,但是它们中的任何一个都无法破坏其状态,并且对象在任何时间点的每个线程都具有相同的状态。
由于我们创建一个不可变对象后,任何人都无法对其进行修改,这使得每个不可变对象默认情况下都是线程安全的。我们不需要对其应用任何线程安全措施,例如创建同步方法。
因此,由于字符串对象具有不变性,因此它可以被多个线程共享,即使它被多个线程操纵,它也不会改变其值。
3.安全性
在每个应用程序中,我们都需要传递几个秘密,例如用户的用户名\密码,连接URL,并且通常所有这些信息都作为字符串对象传递。
现在假设如果String本质上不是一成不变的,那么它将对应用程序造成严重的安全威胁,因为允许更改这些值;如果允许,则由于错误的代码编写或任何其他人可能会更改这些值。可以访问我们的变量引用。
4.类加载
如在Java中使用反射通过示例创建对象中所讨论的那样,我们可以使用Class.forName("class_name")
方法将类加载到内存中,然后再次调用其他方法来这样做。甚至JVM也使用这些方法来加载类。
但是,如果您清楚地看到所有这些方法都将类名作为字符串对象接受,那么在Java类加载中使用Strings,并且不可变性提供了安全性,可以通过加载正确的类ClassLoader
。
假设如果String不会是不可变的,并且我们尝试加载将java.lang.Object
其更改为org.theft.OurObject
两者之间的值,那么现在我们所有的对象都有某种行为,有人可以用它来处理不需要的事情。
5. HashCode缓存
如果我们要对任何对象执行任何与哈希相关的操作,则必须覆盖 hashCode()
方法,并尝试通过使用对象的状态生成准确的哈希码。如果对象的状态正在更改,这意味着其哈希码也应更改。
因为String是不可变的,所以一个字符串对象持有的值永远不会改变,这意味着它的哈希码也不会改变,这使String类有机会在对象创建期间缓存其哈希码。
是的,String对象在创建对象时会缓存其哈希码,这使其成为与哈希相关的操作的最佳选择,因为不需要再次计算哈希码,这可以节省一些时间。这就是为什么String主要用作HashMap
键的原因。
阅读有关Java中为什么String是不可变且最终的更多信息。