23

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

我有两个字符串定义为

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

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

4

9 回答 9

26

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

至于其他语言,这是一个更大的问题,但是有了这些答案中的所有信息,我相信您可以在网络上进行研究。可以说,对于应该如何做到这一点,并没有普遍的共识。

于 2013-04-04T11:14:12.260 回答
8
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 节)计算的字符串在编译时计算,然后将其视为文字。

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

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

于 2013-04-05T07:56:38.773 回答
7

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

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

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

字符串池 是所有已经定义的字符串都存储在某个“池”中的机制,在创建新的字符串对象之前,编译器会检查是否已经定义了这样的字符串。

于 2013-04-04T08:00:56.937 回答
3

例子。

第一个例子

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 创建对象并且具有相同的内容,那么它分配与第一个字符串相同的引用包含.

于 2013-04-04T08:02:20.603 回答
0
String s1="Java";
String s2="Java"; 

它们是否指向相同的内存位置?

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

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

...有点看问题“Java字符串和StringPool”的详细答案

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

没有哈希码取决于字符串的内容

于 2013-04-04T08:02:04.680 回答
0

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

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

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

于 2013-04-04T08:05:19.923 回答
0

当你有

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

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

但如果你有:

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

那么你就有了隐式构造。如果两个字符串字面量具有相同的值,则它们共享相同的存储空间,这是因为 Java 保留了相同字符串的存储空间!(具有相同值的字符串)

于 2013-04-04T08:05:37.197 回答
0
String s1="Java";
String s2="Java";

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

于 2013-04-04T08:46:31.530 回答
-2

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

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

于 2017-03-06T07:31:40.023 回答