字符串a == 字符串b的规则

5

我正在尝试理解字符串池是如何工作的,以及一个字符串与另一个字符串相等的规则。

例如,这段代码:

public static void main(String[] hi){
    String s1 = "lol";
    String s2 = "lol";
    String s3 = new String("lol");
    System.out.println( s1 == s2 );// true
    System.out.println( s2 == s3); // false
    s3.intern(); //line 1
    System.out.println( s1 == s3); // false
    testString(s1);

}

private static void testString(String s1){
    String s4 = "lol";
    System.out.println( s1 == s4); // true
}
  1. 在//line 1:将字符串添加到字符串池中。由于它与s1不相等,我假设字符串池中有一个重复项。正确吗?

  2. 何时会在池中出现重复项?换句话说,即使两个字符串具有相同的字符序列,someString == someString什么情况下会返回false?

注:我无论何时都使用string1.equals(string2)。我只是想更深入地了解基础机制。


1
s3.intern("lol") 的结果应该是一个指向与字符串常量相同实例的引用。 - Hank D
同时参考以下两个链接:https://dev59.com/JWYq5IYBdhLWcg3w2EFi 和 https://dev59.com/fXI_5IYBdhLWcg3wDOdC。 - Tunaki
5个回答

5

为了获得正确的行为,您的s3.intern();应该改为s3 = s3.intern();


3
  1. //line 1:该字符串被添加到字符串池中。由于它与s1不相等,我假设字符串池中存在重复项。这是不正确的吗?

不,不正确。字符串池中已经有一个字符串"lol",因此它不会创建重复项。但是,您没有对调用intern()的返回值进行任何操作,因此s3仍然引用不在池中的String对象。

尝试使用s3 = s3.intern();而不是只使用s3.intern();

  1. 在字符串池中有重复项的规则是什么?换句话说,即使两个字符串具有相同的字符序列,什么情况下someString == someString返回false?

字符串池的整个目的是避免内存中出现重复的字符串,因此字符串池中不会有重复项。


好的,但是关于第二点,无论这两个字符串在哪里创建,s1 == s2 都会评估为true,对吗?假设它们最初都被实例化为:myString =“lol”; - Ced
1
当你执行 String myString = "lol"; 时,你并没有直接实例化一个新的 String 对象。编译器足够聪明,使 myString 引用已经存在于池中的 String,避免创建新的 String 对象。 - Jesper
好的,我只是想知道如果例如导入了JAR文件或类似的东西,行为是否会有所不同。最终这并不重要,我明白大意了。 - Ced

2

比较字符串字面量(如s1和s2)时,它们始终来自池中,与在运行时使用构造函数创建的字符串字面量或字符串相比会有所不同。

当确切存在相同的字面量时,保证从字符串池中获取字符串字面量。字符串对象仍然在内部使用来自池中的字符串,但作为对象具有不同的引用。

String#intern();返回从池中内部使用的字符串的引用。

以下代码片段展示了这种行为:

String literalOne = "abc";
String literalTwo = "abc";
String stringOne = new String("abc");
String stringTwo = new String("abc");

System.out.println("literalOne == literalTwo ? " + (literalOne == literalTwo));
System.out.println("stringOne == stringTwo ? " + (stringOne == stringTwo));
System.out.println("Internally stringOne == stringTwo ? " + (stringOne.intern() == stringTwo.intern()));
System.out.println("Internally stringOne == literalOne ? " + (stringOne.intern() == literalOne));

输出结果:

literalOne == literalTwo ? true
stringOne == stringTwo ? false
Internally stringOne == stringTwo ? true
Internally stringOne == literalOne ? true

2

基本上我们可以通过两种方式创建字符串基本上我们可以通过两种方式创建字符串

String str="some data" (not bound to any object like primitive data type)
String strTwo=new String("some String"); (here strTwo object contains value)

作为名称所示,字符串池是存储在Java堆内存中的一组字符串。我们知道,在Java中,String是一个特殊的类,我们可以使用new运算符创建String对象,也可以使用双引号提供值来创建String对象。
由于Java中的String是不可变的,并且它实现了String intern概念,因此才有了字符串池。字符串池也是享元设计模式的一个例子。
字符串池有助于节省Java运行时的大量空间,尽管创建String需要更多时间。
当我们使用双引号创建一个String时,它首先在字符串池中查找具有相同值的String,如果找到它就返回引用,否则它会在池中创建一个新的String,然后返回引用。
然而,如果使用new运算符,我们强制String类创建一个新的String对象,然后我们可以使用intern()方法将其放入池中或者引用其他具有相同值的池中String对象。
public void method(){
String s1 = "Cat";
        String s2 = "Cat";
        String s3 = new String("Cat");

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

和输出

[![s1 == s2 :true
s1 == s3 :false

1

“==”比较的是引用,所以当你尝试比较s1和s3时,你正在检查这两个引用是否指向同一个对象,但实际上不是,因为你已经创建了s3作为一个新对象(new String("lol"))。因此,现在字符串池中有两个不同的对象,它们恰好具有相同的字符串值(“lol”),在你的代码中,s1、s2、s4指向其中一个,而s3指向另一个。

为了比较每个被引用对象的值,你必须使用equals()方法。


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