Java中的字符串:equals vs ==

时间:2010-07-19 13:28:32

标签: java string reference

  

可能重复:
  How do I compare strings in Java?

  String s1 = "andrei";
  String s2 = "andrei";

  String s3 = s2.toString();

  System.out.println((s1==s2) + " " + (s2==s3));

给出以下代码为什么第二个比较s2 == s3为真? 实际上s2.toString()返回的是什么?实际位于(s2.toString())的位置?

15 个答案:

答案 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,因此s1s2将引用相同的字符串。 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 s1s2都引用相同的String对象,即内容为"andrei"的实体化String。

因此(s1 == s2) && (s1.equals(s2))trueString#toString()不会创建新的String(与String中的许多其他方法一样)但只返回this。因此s2 == s3true

答案 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的共同点很少,所以比较ss1,您可以比较对两个不同对象的引用为什么你得到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个对象。想想你的情况

enter image description here

ss1是不同的对象,但其属性指向toto的同一个实例。

由于您创建它们的方式,它们是不同的对象:

MyString s = new MyString("toto");
MyString s1 = new MyString("toto");

因此s == s1将返回false。要让它返回true,它们必须是同一个对象。你可以这样做:

MyString s = new MyString("toto");
MyString s1 = s;

那就是结果

enter image description here

答案 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)。