简体   繁体   English

为什么不能将字符串“ java”添加到“字符串池”

[英]Why the string 'java' cann't be added to 'String Pool'

The following code confuses me, could anyone explain why the two tests behave differently? 以下代码使我感到困惑,有人可以解释为什么两个测试的行为不同吗? Why does the String comparison in the first test return false while the comparison in the second test returns true ? 为什么第一个测试中的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));
}

You're basically checking whether a string was already in the string pool. 您基本上是在检查字符串池中是否已经有字符串。 The string "java" isn't added to the pool by calling intern in your first piece of code because it's already in the string pool . 字符串“ java”不会通过在您的第一段代码中调用intern来添加到池中, 因为它已经在字符串池中 In each method, your code: 在每种方法中,您的代码:

  • Creates a new string 创建一个新的字符串
  • Calls intern on the newly created string (but ignores the result; almost always a bad idea, and you can detect the existence of a previous value in the string pool easily by using the return value) 在新创建的字符串上调用intern (但是忽略结果;几乎总是一个坏主意,并且您可以使用返回值轻松地检测到字符串池中先前值的存在)
  • Compares the new string with a string literal, which will always use the result that's now in the string pool 将新字符串与字符串文字进行比较,这将始终使用字符串池中现在的结果

Now the call to intern will add the target string to the pool if it doesn't already exist, so your comparison will return true if and only if the new string value was not previously in the string pool. 现在,对intern的调用将把目标字符串添加到该池中(如果该目标字符串尚不存在),因此,当且仅当新字符串值先前不在字符串池中时,您的比较才会返回true。 This is equivalent to testing whether intern returns a different reference to the target of the call. 这等效于测试intern是否返回对调用目标的不同引用。

For any given string reference, there are three possibilities: 对于任何给定的字符串引用,都有三种可能性:

  • That exact reference is present in the string pool already. 该确切的引用已经存在于字符串池中。 (That can't be the case in your code, because you're creating a new string.) (在您的代码中情况并非如此,因为您正在创建一个新字符串。)
  • A reference to an equal string is present in the string pool. 字符串池中存在对相等字符串的引用。 In that case, intern() will return the existing reference. 在这种情况下, intern()将返回现有引用。
  • No equal string is present in the string pool. 字符串池中没有相等的字符串。 In that case, the target of the call will be added to the string pool, and the same reference returned. 在这种情况下,调用的目标将添加到字符串池中,并返回相同的引用。

What you're seeing is the result of other code putting things in the string pool - quite possibly as part of loading classes. 您看到的是其他代码将内容放入字符串池的结果-很可能是加载类的一部分。 Here's an example to demonstrate that: 这是一个示例来证明这一点:

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));
    }
}

Output: 输出:

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

So the interned values are: 因此,中间值是:

java
java.lang
Integer
Test
checkInterned

They're all names that would naturally come up when loading classes (including the one being run). 它们都是在加载类(包括正在运行的类)时自然会出现的名称。

I suspect that "java" is only a special case here in that there may well be lots of code within the JRE that checks whether a string starts with "java" as a reserved name. 我怀疑“ java”在这里只是一种特殊情况,因为JRE中可能有很多代码检查字符串是否以“ java”作为保留名开头。

This doesn't indicate anything about "java" being a keyword though - it's just "a string that's already in the string pool". 但这并不表示“ java”是关键字,它只是“字符串池中已存在的字符串”。 You don't need to treat it any differently. 您无需区别对待。

The first thing to realize is that str1.intern() doesn't change the str1 reference. 首先要意识到的是str1.intern()不会更改str1引用。 It returns the interned reference. 返回被引用的引用。 So if you wanted str1 to now be that reference, you'd have to do: 因此,如果您希望str1现在成为该引用,则必须执行以下操作:

str1 = str1.intern();

So, why the difference? 那么,为什么会有所不同呢? In a nutshell, because the JVM already has a string "java" in its thread pool, because of various internals. 简而言之,由于各种内部因素,JVM在其线程池中已经有一个字符串"java"

In the first example, str1 starts off as a newly instantiated String (as I think you understand). 在第一个示例中, str1以新实例化的String开头(据我认为您理解)。 You then call str1.intern() , which returns the interned reference of a pre-existing String "java", but you don't do anything with that reference. 然后,您调用str1.intern() ,它返回预先存在的String“ java”的内部引用,但是您对该引用不执行任何操作。 When you then compare str1 == "java" , you're comparing the reference to the newly instantiated object with the reference to the interned object, and get false. 然后,当您比较str1 == "java" ,您正在将对新实例化对象的引用与对被隔离对象的引用进行比较,并得到false。

In the second example, "ja1va" does not exist in the string pool to start off. 在第二个示例中,字符串池中不存在"ja1va" When you call str1.intern() , that method puts "ja1va" into the pool, with its current reference (that is, str1 ) as the canonical reference. 当您调用str1.intern() ,该方法将“ ja1va”放入池中,并将其当前引用(即str1 )作为规范引用。 When you subsequently refer to the "ja1va" literal string, the JVM looks to see whether it's already in the pool, sees that it is, and uses it. 当您随后引用"ja1va"文字字符串时,JVM会查看它是否已经在池中,看到它并在使用它。 Thus, you get true. 这样,您就可以实现。

In other words, in the first case, you're creating a new String object and then not actually grabbing its interned equivalent. 换句话说,在第一种情况下,您正在创建一个新的String对象,然后实际上没有抓住它的等效对象。 In the second case, you're creating a new String object, defining that as the interned reference, and then reloading it via a string literal. 在第二种情况下,您将创建一个新的String对象,将其定义为内部引用,然后通过字符串文字重新加载它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM