String s1 = "andrei";
String s2 = "andrei";
String s3 = s2.toString();
System.out.println((s1==s2) + " " + (s2==s3));
给出以下代码为什么第二个比较s2 == s3为真?
实际上s2.toString()返回的是什么?实际位于(s2.toString())
的位置?
答案 0 :(得分:7)
首先String.toString
是无操作:
/**
* This object (which is already a string!) is itself returned.
*
* @return the string itself.
*/
public String toString() {
return this;
}
其次,字符串常量被实现,因此s1和s2在幕后更改为相同的String实例。
答案 1 :(得分:7)
方法String.intern()可用于确保相等的字符串具有相同的引用。字符串常量为intern
,因此s1
和s2
将引用相同的字符串。 String.toString()
只返回自身,也就是说,当a是String时,a.toString()
返回a。所以,s2也== s3。
通常,不应使用引用相等性来比较字符串,而应使用equals()
通过值相等来比较字符串。原因是很容易得到两个相同但不同引用的字符串。例如,在创建子字符串时。此规则的一个例外是,如果您事先知道两个字符串已经intern
(或者您将它们作为比较的一部分实习)。
要回答有关堆或堆栈的隐含问题,请在堆上分配字符串。即使它们被分配在堆栈上,例如即将进行的转义分析和堆栈分配,程序的语义也不会改变,并且您将获得堆和堆栈分配的相同结果。
答案 2 :(得分:7)
基本上当你使用new String("something")
时,你会迫使Java创建一个全新的对象。
当您分配给字符串文字="something"
时,该字符串存储在常量池中,由JVM完成优化。因此,当您为同一个常量指定另一个引用时,存储在常量池中的Object将被重用,基本上,它是同一个对象。
答案 3 :(得分:3)
在比较java字符串时,你应该使用.equals而不是==。
==比较引用,因此s2.ToString()返回s2。
关于堆/堆栈的java词汇表:
In Sun’s JVM, the interned Strings (which includes String literals) are
存储在一个特殊的RAM池中 称为perm gen,JVM所在的位置 还加载类和商店 本机编译的代码。然而 intered Strings的行为没有区别 比他们存储在 普通对象堆。
答案 4 :(得分:3)
来自java虚拟机规范:
字符串文字,更一般地说,作为常量表达式值的字符串是“interned”,以便使用String.intern方法共享唯一的实例。
"andrei"
是一个字符串文字,因此被“实习”。因此,String s1
和s2
都引用相同的String对象,即内容为"andrei"
的实体化String。
因此(s1 == s2) && (s1.equals(s2))
为true
。 String#toString()
不会创建新的String
(与String
中的许多其他方法一样)但只返回this
。因此s2 == s3
是true
。
答案 5 :(得分:2)
为什么第一个结果是假的?
==比较参考文献并创建两个不同的对象。
我理解,对于没有原始类型,当我们做'=='时,
String不是原语。当引用相同的对象时,引用将为==
。
答案 6 :(得分:1)
鉴于==
比较引用,您可以看到s2 == s3
为真,s2.toString()
返回s2。
答案 7 :(得分:1)
由于String是不可变的,所以toString方法的一个有用的实现是 - 在String类中 - 返回它。
例如,我的rt.jar包含以下实现:
public String toString() {
return this;
}
因此,与s3
相关联的引用与与s2
相关联的引用相同。
考虑s1==s2
语句,这是由于对intern()
的所有常量字符串的自动调用。这意味着在编译时,s2初始化代码将被s2=s1
替换,这使得断言非常明显,不是吗?
答案 8 :(得分:1)
第一次比较失败的原因是,您通过调用new
创建了两个对象。现在, ==
运算符会比较两个内存地址,这会产生您获得的返回值,因为这两个对象不在同一个内存单元格中。
它与常量字符串一起工作的原因是,java编译器javac
确实优化了代码。通过该优化,类似的字符串常量被放置在一个和相同的存储单元中。如果您已完成以下操作,那么String
个对象的结果将相同。
String s2 = new String("toto");
String s3 = new String("toto");
System.out.println(s2==s3); //yields false!!
你要走的路是.equals(其他)。 为此,你必须在Mystring类中实现equals方法:
class MyString{
private String s;
public MyString (String s){
this.s = s;
}
public String getContent(){
return s;
}
@Override
public boolean equals(Object other){
if(other instanceof MyString){
MyString compareTo = (MyString) other;
return this.s.equals(compareTo.getContent());
}
return false;
}
}
答案 9 :(得分:1)
对于您应使用String.intern()
的所有其他情况,只有实习(==
)字符串可与equals
进行比较。
在您的情况下,您定义了自己的类型MyString
,它与java String
的共同点很少,所以比较s
和s1
,您可以比较对两个不同对象的引用为什么你得到false
。
答案 10 :(得分:1)
MyString
的每个实例都位于不同的内存位置,因此,忘记实例的内容,对于每两个不同的实例,==
测试将导致false
如果是String
类,当您分配String
变量并且右侧算子是文字(即String s = "foo";
时,会有一个小而重要的区别),只有在以前没有遇到“foo”作为文字时,新的内存位置才会被“foo”占用。如果是这种情况(即String s = "foo"; String otherS = "foo";
),otherS
只会引用已经存在的“foo”。
此行为称为String pooling。
答案 11 :(得分:1)
比较s == s1
时,您正在比较两个不同的MyString
个对象。想想你的情况
s
和s1
是不同的对象,但其属性指向toto
的同一个实例。
由于您创建它们的方式,它们是不同的对象:
MyString s = new MyString("toto");
MyString s1 = new MyString("toto");
因此s == s1
将返回false
。要让它返回true
,它们必须是同一个对象。你可以这样做:
MyString s = new MyString("toto");
MyString s1 = s;
那就是结果
答案 12 :(得分:0)
“==”是比较参考。但是Object类中的Equals()方法只是比较引用。并且子类中方法equals()的行为基于重写此方法的实现。
答案 13 :(得分:0)
在字符串上使用==
时,仅比较引用。因此,==
仅保证在以下情况下返回true
String s1 = "...";
String s2 = s1; // reference assignment!
此处s1 == s2
。在所有其他情况下,==
可能会也可能不会返回true
,即使这两个字符串包含相同的字符序列。
要比较两个字符串的内容,请使用equals()
:
if (s1.equals(s2)) {
...
}
答案 14 :(得分:-2)
s2.toString()返回一个String表示。因为它已经是一个String,它会返回它自己(这就是比较为真的原因)。
所有字符串都在Heap上分配,coparison运算符只是比较它们是否是同一个对象(这就是为什么s1!= s2)。