为什么string在Java中不可变?
我在采访中被问到为什么string是不可变的
我这样回答:
当我们在java中创build一个string像
String s1="hello";
那么会在string池(hello)中创build一个对象, s1将指向hello 。现在如果我们再次做String s2="hello";
那么另一个对象将不会被创build,但s2会指向hello
因为JVM将首先检查是否在string池中存在相同的对象。如果不存在,则只会创build一个新的对象。
现在,如果假设Java允许string可变,那么如果我们将s1更改为hello world
则s2值也将是hello world
所以javastring是不可变的。
请问我的答案是对还是错 ?
String
是不可变的有几个原因,这里是一个总结:
- 安全性 :参数通常在networking连接,数据库连接URL,用户名/密码等中表示为
String
。如果它是可变的,这些参数可以很容易地改变。 - 同步和并发性:使string不可变自动地使它们线程安全,从而解决同步问题。
- caching :当编译器优化你的String对象时,它看到如果两个对象具有相同的值(a =“test”,b =“test”),因此你只需要一个string对象(对于a和b,这两个指向同一个对象)。
- 类加载 :
String
被用作类加载的参数。 如果可变,可能会导致加载错误的类(因为可变对象更改其状态)。
也就是说, String
不变性意味着你不能使用它的公共API来改变它。 实际上,您可以使用reflection绕过正常的API。 在这里看到答案。
在你的例子中,如果String
是可变的,那么考虑下面的例子:
String a="stack"; System.out.println(a);//prints stack a.setValue("overflow"); System.out.println(a);//if mutable it would print overflow
Java开发人员决定由于以下方面devise,效率和安全性,string是不可变的。
devisestring被创build在称为“String Intern pool”的java堆中的特殊存储区中。 当你创build新的string(不是在使用String()构造函数或任何其他string函数内部使用String()构造函数创build一个新的String对象的情况下; String()构造函数总是在池中创build新的string常量,除非我们调用方法intern() )variables,它search池来检查它是否已经存在。 如果存在,则返回现有String对象的引用。 如果string不是不可变的,用一个引用改变string将导致其他引用的错误值。
根据DZone上的这篇文章:
安全string被广泛用作许多Java类的参数,例如networking连接,打开文件等。string不是不可变的,连接或文件将被改变并导致严重的安全威胁。 可变string也可能导致reflection中的安全问题,因为参数是string。
效率string的哈希码经常在Java中使用。 例如,在一个HashMap中。 不可变的保证哈希码总是相同的,这样它就可以被caching而不必担心变化。这意味着,每次使用时都不需要计算哈希码。
根据DZone上这篇文章的最重要的原因:
string常量池 …如果string是可变的,用一个引用改变string将导致其他引用的值不正确。
安全
string被广泛用作许多Java类的参数,例如networking连接,打开文件等。string不是不可变的,连接或文件将被改变并导致严重的安全威胁。 …
希望它会帮助你。
我读了这篇文章为什么string是不可变的或最终在Java中,并假设以下可能是最重要的原因:
string在Java中是不可变的,因为string对象被caching在string池中 。 由于caching的string文字是在多个客户端之间共享的 ,因此一个客户端的操作会影响所有其他客户端的风险始终存在。
string类是FINAL
它意味着你不能创build任何类来inheritance它,并改变基本结构,使斯汀可变。
另外一个String类的实例variables和方法是这样的,你不能一旦创build就改变String
对象。
你添加的原因并不会使string不可变。这一切都说明了string是如何存储在堆中的。string池在性能上也有很大的不同
你是对的。 Java中的String Pool
使用String Pool
文字的概念。 当创build一个string时,如果该string已经存在于该池中,则返回现有string的引用,而不是创build新对象并返回其引用。如果string不是不可变的,则使用一个引用更改string导致其他参考文献的错误值。
我会再添加一件事,因为String
是不可变的,所以对于multithreading是安全的,并且可以在不同的线程中共享一个String实例。 这避免了为了线程安全而使用同步,string隐含地是thread safe
。
string是由Sun微系统不可变的,因为string可以用来存储在地图集合中的键。 StringBuffer是可变的,这是原因,它不能作为地图对象的关键字
主要原因是关于安全
许多密码,数据库连接信息和重要的参数使用“string”发送。
从Security
angular度来看,我们可以使用这个实际的例子:
DBCursor makeConnection(String IP,String PORT,String USER,String PASS,String TABLE) { // if strings were mutable IP,PORT,USER,PASS can be changed by validate function Boolean validated = validate(IP,PORT,USER,PASS); // here we are not sure if IP, PORT, USER, PASS changed or not ?? if (validated) { DBConnection conn = doConnection(IP,PORT,USER,PASS); } // rest of the code goes here .... }
在Java中使string不变的最重要的原因是安全考虑。 接下来是caching 。
我相信这里给出的其他原因,比如效率,并发性,devise和string池,都源于string不可变的事实。 例如。 string池可以被创build,因为string是不可变的,而不是相反。
在这里查看戈斯林采访logging
从战略的angular度来看,他们更倾向于无故障。 通常情况下,你可以用不可变的东西来做这些事情,例如caching结果。 如果将string传递给文件打开方法,或者将string传递给用户界面中标签的构造函数,则在某些API中(如在许多Windows API中)传递一组字符。 该对象的接收者确实需要复制它,因为他们不知道它的存储生命周期。 而且他们不知道这个东西发生了什么,它是否正在改变。
你最终几乎被迫复制对象,因为你不知道你是否拥有它。 关于不可改变的对象的好处之一就是答案是:“是的,你当然是。” 因为所有权问题,谁有权改变,所以不存在。
强制string不变的一件事是安全。 你有一个文件打开的方法。 你传递一个string给它。 然后,在进行操作系统调用之前,它正在进行各种身份validation检查。 如果你设法做一些有效改变string的东西,在安全检查之后,在OS调用之前,然后繁荣,你就进来了。但是string是不变的,所以这种攻击是行不通的。 这个确切的例子是真正要求string是不可变的