String.equals与==的区别

584

这段代码将一个字符串分解成令牌,并将它们存储在一个字符串数组中,然后将一个变量与第一个home进行比较......为什么它不起作用?

public static void main(String...aArguments) throws IOException {

    String usuario = "Jorman";
    String password = "14988611";

    String strDatos = "Jorman 14988611";
    StringTokenizer tokens = new StringTokenizer(strDatos, " ");
    int nDatos = tokens.countTokens();
    String[] datos = new String[nDatos];
    int i = 0;

    while (tokens.hasMoreTokens()) {
        String str = tokens.nextToken();
        datos[i] = str;
        i++;
    }

    //System.out.println (usuario);

    if ((datos[0] == usuario)) {
        System.out.println("WORKING");
    }
}

2
请看一下这个链接:http://www.thejavageek.com/2013/07/27/string-comparison-with-equals-and-assignment-operator/ - Prasad Kharkar
3
"=="比较的是引用而不是内容。将"datos[0] == usuario"改为"datos[0].equals(usuario)"可以得到正确的答案。" - mani deepak
2
我看到你改变了你接受的答案 - 我敦促你阅读我的评论并重新考虑。现在被接受的答案中的“故事”可能一开始看起来不错,但在我看来,它真的经不起审查。 - Alnitak
大多数语言都是这样工作的,只是你看不到,因为大多数语言都有运算符重载,而字符串的==运算符已被重载以调用语言等效的string.equals。Java没有运算符重载,所以你必须像穴居人一样费力地去做。Java在比较字符串时也不会抱怨或警告你,所以它变成了一个运行时错误,你需要去追踪。为什么Java没有运算符重载?因为他们想保持语言简单,然后他们意识到它太简单了,使得处理日期变得复杂。 - LordWabbit
20个回答

608
使用 string.equals(Object other) 函数来比较字符串,而不是使用 == 运算符。该函数检查字符串的实际内容,== 运算符则检查对象引用是否相等。请注意,字符串常量通常会被“池化(interned)”,因此具有相同值的两个常量可以使用 == 进行比较,但最好不要依赖于此。
if (usuario.equals(datos[0])) {
    ...
}

注意:比较是基于“usuario”进行的,因为在您的代码中这是非空的,尽管您仍应该检查您是否实际上已经在 datos 数组中获取了一些标记,否则您将获得一个越界异常。


10
@mo:正如你可以从这个例子中看到的那样,它并没有被平等处理。 - user unknown
57
在某些情况下,甚至在Java中,“==”操作符可能会误导。Java会缓存字符串,因此像这样的代码会返回true:"String a = "Hello"; String b = "Hello"; a == b",即使通常期望的结果是false。 - Jon Taylor
7
@JonTaylor你的例子与缓存无关。在Java代码中键入“Hello”实际上会在类中创建一个匿名静态字符串对象。如果您使用的是好的编译器,“Hello”的多个出现可能会使用相同的静态字符串对象,但将“Hello”替换为 new String(new char[] {'H', 'e', ...}) 同样有效。 - parasietje
10
JLS保证多次出现的“Hello”(作为字面量)引用同一个String对象。编译器无法将其替换为其他内容。 - fgb
5
当比较字符串时,使用==需要非常小心,除非两个字符串都是用字面值初始化的,否则==将始终返回false。例如,以下代码将输出falseString a = "abc"; String b = "abcd"; a += "d"; System.out.println(a == b); - Galax
显示剩余8条评论

593

遇见Jorman

Jorman是一位成功的商人,有两套房子。

图片描述

但其他人并不知道这一点。

是否同一个Jorman?

当你问Madison或Burke街的邻居时,他们唯一能说的就是:

图片描述

仅凭住址,很难确认它是同一个Jorman。因为它们是两个不同的地址,自然会认为这是两个不同的人。

这就是运算符==的行为方式。因此,它将说datos [0] == usuario是false,因为它只比较地址

调查员解救了我们

如果我们派出了一名调查员呢?我们知道那是同一个Jorman,但我们需要证明它。我们的侦探将仔细研究所有物理方面。通过彻底的调查,代理人将能够得出结论,无论是同一个人还是不同的人。让我们看看在Java术语中发生了什么。

这是String的equals()方法的源代码:

图片描述

它逐个字符比较字符串,以确定它们是否相等。

这就是String equals方法的行为方式。因此,datos [0] .equals(usuario)将返回true,因为它执行了逻辑比较


27
我喜欢直观的例子,而这个例子是我见过最好的之一。初学者程序员可以轻松地通过阅读此内容了解背后发生的事情。 - Nader Ghanbari
15
我认为这个回答真的很令人困惑,因为它混淆了人的“名称”和“本身”。它还混淆了“相等性”和“等价性”。this == anObject检查语义上完全是测试是否比较了同样的两个对象(相等性),因此根据定义必须是等价的。while循环后的最终return true并不意味着我们拥有相同的“Jorman”,而是意味着两个实体共享相同的值(等价),这并不意味着它们是相等的。(在这方面,Java的.equals方法命名错误)。 - Alnitak
3
@DavidT.,你忘记检查指纹了 :) - Raymond Chenon
2
-1,因为没有手绘的红圈 - 而且,对于Jorman这个家伙,+1。 - user719662
1
这个比喻可以用于展示==(引用相等)。但它不适合用于展示string.equals()(值相等/等价),因为Jorman只是一个人而且是同一个人。为了正确演示string.equals(),比喻应该是将两个房子的所有属性进行比较(尺寸、颜色等)。如果它们全部匹配,则它们是等价的(string.equals() == true),即使它们是两幢独立的房子。 - wisbucky
显示剩余4条评论

102

需要注意的是,在某些情况下使用"=="运算符可以导致期望的结果,因为Java处理字符串的方式-字符串文字在编译期间被集中处理(请参见String.intern())。因此,当您在两个类中写入例如"hello world"并使用“==”比较这些字符串时,您可能会得到预期的结果:true,这是根据规范所预期的; 当您比较相同的字符串(如果它们具有相同的值)时,第一个字符串是字符串文字(即通过"i am string literal"定义),而第二个字符串是在运行时构造的,即使用"new"关键字,例如new String("i am string literal"),等于(相等)运算符返回false,因为它们都是String类的不同实例。

唯一正确的方法是使用.equals() -> datos[0].equals(usuario)==只表示两个对象是否为同一对象实例(即具有相同的内存地址)

更新:2013年4月1日,由于下面的评论有些正确,我更新了这篇文章。虽然它确实节省了内存资源(这是我所说的“优化”),但它主要是语言功能。


6
这实际上不仅是JVM优化的副作用,与编译器毫无关系。根据Java VM规范,所有类中静态字符串(字面量)的标识都是保证一致的,并且适用于至少兼容Java 1.1的所有虚拟机。 - x4u
7
如果你是指JVM规范第2.3章“字符串字面量和常量表达式的值通常会被“interned”,以便共享唯一实例,使用String.intern方法”。那么根据规范,这是由JVM保证的。但对我来说,这仍然意味着优化。据我所知,这没有语义价值。另一方面,“==”具有“标识相等”的语义,而equals()方法具有“对象相等”的语义,因此您应该遵守这一点,不要依赖于JVM规范,这只是JVM实现者而非开发人员(他们有Java语言规范)的指南。 - Michal Bernhard
3
为了符合规范,文字字面值、类名等都被汇编在一起,这不仅仅是为了优化。"xxx" 永远等于 "xxx",这是语言设计的一部分,而不是实现细节或指导方针。 - bestsss
1
实际上,使用==运算符总是返回预期的结果。问题在于有些人对结果有错误的期望。这不是语言的问题,而是他们缺乏知识的问题。如果他们期望==总是返回与.equals相同的结果,那么他们应该重新阅读教科书。另一方面,如果他们期望==返回引用比较,他们会注意到它总是返回他们所期望的结果。 - Stultuske

38

equals()函数是Object类的一个方法,应该被程序员覆盖。 String类重写它来检查两个字符串是否相等,即内容相等而不是引用相等。

==运算符检查对象的引用是否相同。

考虑以下程序:

String abc = "Awesome" ;
String xyz =  abc;

if(abc == xyz)
     System.out.println("Refers to same string");

这里的abcxyz都指代同一个String "Awesome"。因此表达式(abc == xyz)true

String abc = "Hello World";
String xyz = "Hello World";

if(abc == xyz)
    System.out.println("Refers to same string");
else
    System.out.println("Refers to different strings");

if(abc.equals(xyz))
     System.out.prinln("Contents of both strings are same");
else
     System.out.prinln("Contents of strings are different");

这里abcxyz是两个不同的字符串,但内容相同,都是"Hello World"。因此,表达式(abc == xyz) 的值为false,而 (abc.equals(xyz)) 的值为true

希望您理解了 ==<Object>.equals() 之间的区别。

谢谢。


我在想,在什么(不明显的)情况下,abc == xyz会起作用? - Chibueze Opata
7
在将 prinln 修正为 println 后,该代码的输出为:“引用同一字符串,两个字符串的内容相同,即 (abc == xyz) 和 (abc.equals(xyz)) 均为 true!” - arun
6
如上所述,这个答案是错误的。因为有时候由于内部优化机制,内容相同的两个字符串实际上只被表示为一个对象。这种优化是可能的,因为字符串是不可变的。 - tObi
https://dev59.com/DnRB5IYBdhLWcg3wyqEd#513839 这里有详细的答案! - mavis

38
The == operator checks if the two references point to the same object or not.
.equals() checks for the actual string content (value).

注意,.equals() 方法属于 Object 类(所有类的超类)。您需要根据您的类要求来覆盖它,但对于 String 类,它已经被实现,并且它会检查两个字符串是否具有相同的值。

Case1)
String s1 = "Stack Overflow";
String s2 = "Stack Overflow";
s1 == s1;      // true
s1.equals(s2); // true
Reason: String literals created without null are stored in the string pool in the permgen area of the heap. So both s1 and s2 point to the same object in the pool.
Case2)
String s1 = new String("Stack Overflow");
String s2 = new String("Stack Overflow");
s1 == s2;      // false
s1.equals(s2); // true
Reason: If you create a String object using the `new` keyword a separate space is allocated to it on the heap.

这是最简单的答案,清晰地展示了不同的情况。 - wisbucky
1
我想在Case2中再加入两个例子:s1 == "Stack Overflow" // falses1.equals("Stack Overflow") // true。这展示了将对象与字面量进行比较的情况。 - wisbucky

29

== 用于测试引用相等性。

.equals() 用于测试值的相等性。

因此,如果您想要测试两个字符串是否具有相同的值,应该使用.equals()(除了在一些情况下,您可以保证具有相同值的两个字符串将由同一个对象表示,例如:String 内部化)。

== 用于测试两个字符串是否是相同的Object

// These two have the same value
new String("test").equals("test") ==> true 

// ... but they are not the same object
new String("test") == "test" ==> false 

// ... neither are these
new String("test") == new String("test") ==> false 

// ... but these are because literals are interned by 
// the compiler and thus refer to the same object
"test" == "test" ==> true 

// concatenation of string literals happens at compile time resulting in same objects
"test" == "te" + "st"  ==> true

// but .substring() is invoked at runtime, generating distinct objects
"test" == "!test".substring(1) ==> false

需要注意的是,==equals()更加高效(仅需进行一次指针比较而非循环),因此在适用的情况下(即可以保证只使用内部字符串时)它能够提供重要的性能优化。然而,这种情况很少发生。


这是我迄今为止看到的对这个问题最简单的答案..谢谢。 - Swadhikar
关于子字符串,如果您使用==操作符比较两个相同的字符串,并且其中一个是另一个的子字符串,则==会返回true。例如,以下代码(在我测试时)将打印true:String str = "abcdef"; System.out.println(str == str.substring(0, str.length())); - MinteZ

26

不要使用

datos[0] == usuario
datos[0].equals(usuario)

== 比较变量的引用,而 .equals() 比较的是值,这正是您所需要的。


11
请确保左侧不为空。 - Steve Kuo
或者使用usario.equals,就像@Alnitak所示的选定答案一样。如果您最初知道usario不为null,那么它可以节省您一步(或很多步)。 - user1499731

12

让我们分析以下Java代码,以了解字符串的身份和相等性:

public static void testEquality(){
    String str1 = "Hello world.";
    String str2 = "Hello world.";

    if (str1 == str2)
        System.out.print("str1 == str2\n");
    else
        System.out.print("str1 != str2\n");

    if(str1.equals(str2))
        System.out.print("str1 equals to str2\n");
    else
        System.out.print("str1 doesn't equal to str2\n");

    String str3 = new String("Hello world.");
    String str4 = new String("Hello world.");

    if (str3 == str4)
        System.out.print("str3 == str4\n");
    else
        System.out.print("str3 != str4\n");

    if(str3.equals(str4))
        System.out.print("str3 equals to str4\n");
    else
        System.out.print("str3 doesn't equal to str4\n");
}

当第一行代码 String str1 = "Hello world." 执行时,会创建一个字符串 \Hello world.",并且变量 str1 指向它。由于优化的缘故,下一行代码执行时不会再次创建另一个字符串 "Hello world."。变量 str2 也指向现有的 ""Hello world."
操作符 == 检查两个对象的身份(即两个变量是否指向同一个对象)。由于 str1str2 指向内存中的同一个字符串,它们是相同的。方法 equals 检查两个对象的相等性(即两个对象是否具有相同的内容)。当然,str1str2 的内容是相同的。
当代码 String str3 = new String("Hello world.") 执行时,会创建一个具有内容 "Hello world." 的新字符串实例,并由变量 str3 引用它。接着,又创建了另一个具有内容 "Hello world." 的字符串实例,并由 str4 引用。由于 str3str4 引用了两个不同的实例,它们是不同的,但它们的内容相同。
因此,输出包含四行:
Str1 == str2

Str1 equals str2

Str3! = str4

Str3 equals str4

你不应该为你的类覆盖equals方法。在某些情况下,你可能会这样做,但并非总是如此。为什么我要在我的XyPanel或FooDialog中重写equals方法呢? - user unknown

11

如果在将字符串插入数组之前对其调用intern(),它也会起作用。 如果值相等则经过内部化的字符串是引用相等(==),反之亦然(equals())。

public static void main (String... aArguments) throws IOException {

String usuario = "Jorman";
String password = "14988611";

String strDatos="Jorman 14988611";
StringTokenizer tokens=new StringTokenizer(strDatos, " ");
int nDatos=tokens.countTokens();
String[] datos=new String[nDatos];
int i=0;

while(tokens.hasMoreTokens()) {
    String str=tokens.nextToken();
    datos[i]= str.intern();            
    i++;
}

//System.out.println (usuario);

if(datos[0]==usuario) {  
     System.out.println ("WORKING");    
}

11

您应该使用 字符串相等方法 来比较两个字符串是否相等,而不是仅比较引用的运算符 ==。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接