以下代码让我感到困惑,任何人都可以解释为什么这两个测试表现不同?为什么第一个测试中的String比较返回false而第二个测试中的比较返回true?
public class Student {
/**
* Why the string "java" doesn't added to the 'String Pool' by intern() method ?
*/
@Test
public void test1() {
String str1 = new String("ja") + new String("va");
str1.intern();
String str2 = "java";
// Result:false
System.out.println("Result:" + (str1 == str2));
}
/**
* Any other strings will be added to 'String Pool' as expected after intern() is invoked.
*/
@Test
public void test2() {
String str1 = new String("ja1") + new String("va");
str1.intern();
String str2 = "ja1va";
// Result:true
System.out.println("Result:" + (str1 == str2));
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 16
您基本上检查字符串池中是否已存在字符串.通过调用intern第一段代码,字符串"java"不会添加到池中,因为它已经在字符串池中.在每种方法中,您的代码:
intern新创建的字符串(但忽略结果;几乎总是一个坏主意,并且您可以通过使用返回值轻松检测字符串池中是否存在先前的值)现在调用intern将把目标字符串添加到池中(如果它尚不存在),因此当且仅当新字符串值以前不在字符串池中时,您的比较才会返回true .这相当于测试是否intern返回对调用目标的不同引用.
对于任何给定的字符串引用,有三种可能性:
intern()将返回现有的引用.您所看到的是其他代码将内容放入字符串池的结果 - 很可能是加载类的一部分.这是一个证明:
public class Test {
public static void main(String... args) {
checkInterned("ja", "va");
checkInterned("ja", "va.lang");
checkInterned("ja", "va.other");
checkInterned("Int", "eger");
checkInterned("abc", "def");
checkInterned("Te", "st");
checkInterned("Te", "st2");
checkInterned("check", "Interned");
checkInterned("check", "Interned2");
}
public static void checkInterned(String start, String end) {
String x = start + end;
String y = x.intern();
System.out.println(x + " was interned already? " + (x != y));
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
java was interned already? true
java.lang was interned already? true
java.other was interned already? false
Integer was interned already? true
abcdef was interned already? false
Test was interned already? true
Test2 was interned already? false
checkInterned was interned already? true
checkInterned2 was interned already? false
Run Code Online (Sandbox Code Playgroud)
所以实习值是:
java
java.lang
Integer
Test
checkInterned
Run Code Online (Sandbox Code Playgroud)
它们都是在加载类(包括正在运行的类)时自然会出现的名称.
我怀疑"java"只是一个特例,因为JRE中可能有很多代码检查字符串是否以"java"作为保留名称开头.
这并不表示"java"是关键字的任何内容 - 它只是"已经在字符串池中的字符串".您不需要以不同的方式对待它.
要意识到的第一件事是str1.intern()不改变str1参考.它返回实习参考.所以,如果你str1现在想成为那个参考,你必须这样做:
str1 = str1.intern();
Run Code Online (Sandbox Code Playgroud)
那么,为什么差异呢?简而言之,因为JVM "java"在其线程池中已经有一个字符串,因为它有各种内部结构.
在第一个示例中,str1以新实例化的String开头(我认为您理解).然后调用str1.intern(),返回预先存在的字符串"java"的实习引用,但是您不对该引用执行任何操作.然后str1 == "java",当您比较时,您将对新实例化对象的引用与对实习对象的引用进行比较,并获得false.
在第二个示例中,"ja1va"字符串池中不存在要启动.当你调用时str1.intern(),该方法将"ja1va"放入池中,并将其当前引用(即str1)作为规范引用.当您随后引用"ja1va"文字字符串时,JVM会查看它是否已经在池中,看到它是否存在并使用它.因此,你得到了真实.
换句话说,在第一种情况下,您正在创建一个新的String对象,然后实际上并没有抓住它的实例等价物.在第二种情况下,您将创建一个新的String对象,将其定义为interned引用,然后通过字符串文字重新加载它.
| 归档时间: |
|
| 查看次数: |
198 次 |
| 最近记录: |