Java中的字符串:equals vs == [duplicate]

  可能重复:   如何比较Java中的字符串?
  String s1 = "andrei";
  String s2 = "andrei";

  String s3 = s2.toString();

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

要回复问题请先登录注册