【问题标题】:What makes reference comparison (==) work for some strings in Java?是什么让参考比较 (==) 对 Java 中的某些字符串起作用?
【发布时间】:2012-03-30 16:38:54
【问题描述】:

我有以下几行代码来比较字符串。 str1 不等于 str2,这是可以理解的,因为它比较对象引用。但是为什么 s1 等于 s2 呢?

String s1 = "abc";
String s2 = "abc";

String str1 = new String("abc");
String str2 = new String("abc");

if (s1==s2)
    System.out.println("s1==s2");           
else
    System.out.println("s1!=s2");

if (str1==str2)
    System.out.println("str1==str2");           
else
    System.out.println("str1!=str2");

if (s1==str1)
    System.out.println("str1==s1");         
else
    System.out.println("str1!=s1");

输出:

  s1==s2
  str1!=str2
  str1!=s1 

【问题讨论】:

标签: java string equals


【解决方案1】:

字符串常量池本质上会缓存所有字符串文字,因此它们是下面的同一个对象,这就是为什么您会看到为s1==s2 所做的输出。它本质上是 VM 中的一种优化,以避免在每次声明文字时创建一个新的字符串对象,这可能很快就会变得非常昂贵!在您的 str1==str2 示例中,您明确告诉虚拟机创建新的字符串对象,因此它是错误的。

顺便说一句,在任何字符串上调用intern() 方法都会将其添加到常量池中,只要不存在等效的字符串(并返回它添加到池中的字符串。)它不是这样做肯定是个好主意,除非您确定要处理肯定会用作常量的字符串,否则您最终可能会造成难以追踪的内存泄漏。

【讨论】:

  • +1 但还值得注意的是,将大量字符串添加到常量池通常是一个坏主意,因为它会导致难以检测的内存泄漏。仅对少量固定的字符串执行此操作,这些字符串真正将用作常量(例如 HashMap 键),绝不用于任意字符串数据。
  • @mikera 同意 - 我把它作为学术观点而不是其他任何东西 :)
【解决方案2】:

s1 和 s2 是字符串文字。当您创建一个新的字符串字面量时,编译器首先检查字符串池中是否存在任何表示相同的字面量。如果存在,则编译器返回该文字,否则编译器创建一个新文字。

当您创建字符串s2 时,编译器会从池中返回字符串s1,因为它之前已经创建。这就是s1s2 相同的原因。这种行为称为interning

【讨论】:

  • 当你说“编译器创建一个新的”时,我有点困惑。 AFAIK,编译器用于创建中间机器代码,实际上并不在内存中运行(因此创建)任何对象。您的意思是编译器 replaces 字符串文字吗?请澄清这一点。
  • 我现在没有任何支持文档,但我相信@Chandra Sekhar 指的是 JIT 或 Just In Time 编译器,而不是 javac 编译器。
【解决方案3】:

这种现象是由于String interning.

基本上,所有字符串文字都被“缓存”并被重复使用。

【讨论】:

    【解决方案4】:

    这是由于字符串文字被实习。在这件事上,Java documentations 说:

    所有文字字符串和字符串值常量表达式都是 实习

    这就解释了为什么s1s2 是相同的(这两个变量指向同一个内部字符串)

    【讨论】:

      【解决方案5】:

      在 Java 中,相同的常量字符串将被重用。这样s1s2 指向同一个“abc”对象和s1==s2。但是当你使用new String("abc") 时,会创建另一个对象。所以s1 != str1

      【讨论】:

        【解决方案6】:

        由于string 在java 中是不可变的,所有string literals 都被缓存以实现可重用性。

        当你使用 new() 操作符创建 String 对象时,它总是在堆内存中创建一个新对象。另一方面,如果您使用字符串文字语法创建对象,例如“Java”,如果它已经存在,它可能会从字符串池(Perm gen 空间中的字符串对象缓存,现在在最近的 Java 版本中移动到堆空间)中返回一个现有对象。否则它将创建一个新的字符串对象并放入字符串池以供将来重复使用。

        String s1 = new String("java");
        String s2 = new String("java");
        String s3 = "java";
        String s4 = "java";
        

        Please refer this link

        【讨论】:

          猜你喜欢
          • 2017-05-10
          • 1970-01-01
          • 2015-01-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多