两个具有相同内容的字符串是否会存储在同一个内存位置?

这是我在采访中遇到的一个问题。

我把两个字符串定义为

String s1="Java"; String s2="Java"; 

我的问题是这两个引用是否指向相同的内存位置。 通常,当我们创建相同的字符串(没有new关键字)时,内容是否只存储在内存中一次,所有具有相同内容的String对象只是引用相同的位置,而不是冗余地存储字符串“Java”? s1和s2的哈希码是相同的。 但是哈希码是否直接依赖于对象的内存位置?

组合相同字符串的过程称为“ 实习 ”,并且已经被许多语言编译器多年来完成,但并非总是如此。 这个问题的答案,特别是由@ GennadyVanin – Novosibirsk扩展,取决于语言和编译器的实现。 对于Java,根据Java语言规范的要求,所有常量字符串都是实例化的。 但这只是常量字符串表达式,并且只有在它们同时编译时才会出现。 如果您有两个在时间和空间上充分分离的Java字符串( 例如 ,编译成单独的JAR文件),则它们将不是同一个对象。 类似地,动态创建的Java字符串( 例如 ,各种toString()方法的输出)将不会被实现,除非该方法通过String.intern()特定地请求它。 是的,一个实习字符串的所有使用将共享相同的内存位置 – 这是为什么字符串首先被实现的主要原因。

至于其他语言,这是一个更大的问题,但是通过这些答案中的所有信息,我相信你可以在网上研究它。 我只想说就如何做到这一点没有普遍的一致意见。

 String s1="Java"; String s2="Java"; My question is whether these two references point to the same memory location 

哑巴引用Java语言规范的§3.10.5 :

字符串文字是对String类实例的引用( §4.3.1 , §4.3.3 )。

此外,字符串文字始终引用类String的相同实例。 这是因为字符串文字 – 或者更常见的是作为常量表达式(第15.28节 )的值的字符串 – 被“实例化”以便使用String.intern方法共享唯一实例。

并阅读注释代码示例:

这个例子说明了六点:

  • 同一个包(第7节)中同一个类(第8节)中的文字字符串表示对同一个String对象的引用(第4.3.1节)。

  • 同一个包中不同类中的文字字符串表示对同一String对象的引用。

  • 不同包中不同类中的文字字符串同样表示对同一String对象的引用。

  • 由常量表达式计算的字符串(第15.28节)在编译时计算,然后将其视为文字。

  • 在运行时通过串联计算的字符串是新创建的,因此是不同的。

  • 显式实例化计算字符串的结果与具有相同内容的任何预先存在的文字字符串相同。

当编译器优化您的字符串文字时,它会看到s1和s2都具有相同的值,因此您需要一个字符串对象 。 它是安全的,因为String在Java中是不可变的。

 String s1="Java"; String s2="Java"; System.out.println(s1== s2); 

这给出了结果,因为s1s2指向同一个对象。

字符串池是一种机制,所有已定义的字符串都存储在某个“池”中,并且在创建新的String对象编译器之前检查是否已定义此类字符串。

例。

第一个例子

 String s1 = "FirstString"; String s2 = "FirstString"; if(s1 == s2) { //This condition matched true because java don't make separate object for these two string. Both strings point to same reference. } 

第二个例子

 String s1= "FirstString"; String s2 = new String("FirstString"); if(s1.equals(s2)) { //This condition true because same content. } if(s1 == s2) { //This condition will be false because in this java allocate separate reference for both of them } 

结论:Java检查字符串是否存在。 如果我们使用new创建第二个字符串的对象并且具有不同的内容,那么它创建对象并分配不同的引用。如果我们不使用new创建对象并且具有相同的内容,则它分配与第一个字符串包含相同的引用。

添加到其他人:新关键字总是强制创建新对象。 如果您声明如下:

 String s1 = "some"; String s2 = "some"; 

然后使用字符串池机制,引用s1和s2将引用具有值“some”的相同String对象。

当你有

 String str1 = new String("BlaBla"); //In the heap! String str2 = new String("BlaBla"); //In the heap! 

然后你通过new运算符(和构造函数)显式创建一个String对象。 在这种情况下,您将使每个对象指向不同的存储位置。

但如果你有:

 String str1 = "BlaBla"; String str2 = "BlaBla"; 

然后你隐含了构造。 如果两个字符串文字具有相同的值,则它们共享相同的存储空间 ,这是因为Java保留了相同字符串的存储空间! (具有相同值的字符串)

 String s1="Java"; String s2="Java"; 

两者都指向同一个对象。 更多详情请点击这里

 String s1="Java"; String s2="Java"; 

他们指向相同的内存位置吗?

我最初说“不”,但在上面的情况下,请参阅下面提到的StringPool答案,它实际上是肯定的..

“当我们创建相同的字符串(没有new关键字)时,内容是否只存储在内存中一次,所有具有相同内容的String对象只引用相同的位置”

…有点看到详细的答案“Java Strings and StringPool”

“s1和s2的哈希码是相同的。但是哈希码是否直接依赖于对象的内存位置?”

没有哈希码取决于String的内容

是的,Andrew Hare在这个链接https://stackoverflow.com/a/2486195/4835894上回答了堆栈溢出问题。

基本上,字符串实习池允许运行时通过保留池中的不可变字符串来节省内存,以便应用程序的区域可以重用公共字符串的实例,而不是创建它的多个实例。