【问题标题】:String.intern() how to workString.intern() 如何工作
【发布时间】:2014-11-22 09:57:44
【问题描述】:
package com.zhb.jvm;

/**
 * 
 * @author zhb
 *
 */
public class RuntimeConstantPoolOOM {
    public static void main(String[] args){

        String str1 = "abc";
        System.out.println(str1.intern() == str1);    //true
        String str2 = new String("abcd");
        System.out.println(str2.intern() == str2);    //false

        String str3 =new StringBuilder("math").append("analyze").toString();
        System.out.println(str3.intern() == str3);    //true

        String str4 =new StringBuilder("computer").append("software").toString();
        System.out.println(str4.intern() == str4);    //true

        String str5 =new StringBuilder("jav").append("a").toString();
        System.out.println(str5.intern() == str5);    //false
    }

}

首先,我们可以知道intern()方法的定义。 intern 的定义:当调用 intern 方法时,如果池中已经包含一个等于 thisString 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串。否则,将 thisString 对象添加到池中并返回对该 String 对象的引用。

str1.intern == str1 是真的。这很容易理解。 str2.intern() == str2 这个通过方法的定义也很容易理解。 但是为什么 str3.intern() == str3 是真的。其实我认为按照定义是假的。 相反,str5.intern() == str5 是假的。 我在终端中运行命令 java版本 java版本“1.7.0_40” Java(TM) SE 运行时环境 (build 1.7.0_40-b43) Java HotSpot(TM) 64 位服务器 VM(内部版本 24.0-b56,混合模式)

我想获得正确答案。非常感谢!

【问题讨论】:

  • 重新打开 - 尚不清楚 marked duplicate 如何解决此处观察到的行为...(至少,它肯定不明显...)

标签: java string


【解决方案1】:

为什么 str3.intern() == str3 是真的

因为,正如你所说:

否则,将thisString对象添加到池中并返回对该String对象的引用。

你就是这种情况。该池尚不包含 str3(即“mathanalyze”)。所以 str3 被添加到池中并返回。

对于 str5,你是另一种情况:

如果池中已经包含一个等于 thisString 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串

所以,当你的代码执行时,池中已经包含字符串“java”,这并不奇怪,因为 java 是所有标准类的顶级包的名称,也是用于启动 JVM 的可执行文件。在执行 main 方法之前,在引导应用程序和加载类的代码中使用文字字符串“java”的可能性很大。

【讨论】:

  • 这不是和你描述的相反吗?如果池确实包含字符串,str3.intern() == str3true
  • 没有。 如果池中已经包含一个等于 thisString 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串。否则,会将 thisString 对象添加到池中并返回对此 String 对象的引用。。 “java”已经在池中,所以返回池化的“java”。 "mathanalyze" 不在池中,因此传递给 intern() 的字符串被添加到池中并返回。
  • @OliverCharlesworth :它包含它,但在调用 intern() 之前没有包含它。
  • @JBNizet:我一定累了!按照我的逻辑:如果池中还没有“mathanalyze”,那么str3.intern() 会将其添加到池中并返回一个 new 引用,该引用不等于旧引用。因此,平等应该评估为false
  • @OliverCharlesworth :它不会返回新的引用,这就是你弄错了。这不合逻辑,因为在池中添加字符串的原则是允许对其进行单一引用。
【解决方案2】:

在我看来,我会说字符串 "java" 默认情况下在池中。事实上,当您在str3 上调用intern() 时,这个词还没有在池中,所以它被添加并且返回的引用是str3(没有创建新对象),所以测试结果为真。反之,"java"已经在池中,所以返回池中对象的引用,与str5的引用不同。

请注意,您不仅会观察到 "java" 的相同行为,还会观察到所有单个字符。

【讨论】:

  • 你是对的,但稍有偏差。默认情况下,池中没有任何内容;只是在运行您的代码之前,JVM 也会运行许多其他代码,而java 会更快地被实习。但这已经改变了,在java-11 this 中运行:String notInPool = new String(new char[] { 'j', 'a', 'v', 'a' }); String inPool = "java2"; System.out.println(new String(notInPool).intern() == notInPool); System.out.println(new String(inPool).intern() == inPool); 只会为第二个打印true...
  • 是的,这是有道理的。感谢有关 Java 11 的有趣事实!你认为它的改变是因为一个愚蠢的重构,比如删除一个静态常量或阻止某些类加载直到它们是必要的,还是有更深层次的原因,比如改变实习本身的行为?
  • 老实说:我完全不知道
【解决方案3】:

测试foo.intern() == foo 是一种在调用intern() 之前测量foo 是否已经在池中的方法。所以str3.intern() == str3 表示str3 还没有在池中。就是这样。

在运行时的某个地方可能有一个常量字符串"java" 在您复制之前被加载到池中,这就是str5.intern() != str5 的原因。

以下是我通过 grepping OpenJDK 8 源代码发现的最明显的案例:

./com/sun/beans/decoder/DocumentHandler.java:        setElementHandler("java", JavaElementHandler.class); // NON-NLS: the element name
./com/sun/tools/example/debug/gui/JDBMenuBar.java:        JDBFileFilter filter = new JDBFileFilter("java", "Java source code");
./com/sun/tools/jdi/SunCommandLineLauncher.java:                "java",
./sun/launcher/LauncherHelper.java:                (progname == null) ? "java" : progname ));
./sun/rmi/server/Activation.java:                        File.separator + "bin" + File.separator + "java";
./sun/rmi/server/Activation.java:                    command[0] = "java";

【讨论】:

  • 这并不是一个真正的通用检查(它在两种情况下都返回true 1 3)。
  • @OliverCharlesworth 对,编译时常量字符串有这种特殊情况。
  • 它也会在您执行str3.intern() == str3 时间返回true,即使它现在肯定在池中。
【解决方案4】:

据我了解,可能有任何运行时字符串 "java" 会导致此问题。

    String str5 =new StringBuilder("jav").append("a").toString();
    System.out.println(str5.intern() == str5);    //false

以上这两个语句只是false,除了你想用true形成它传递的任何其他字符串。

Intern () 符合 Java 规范。

这将返回字符串对象的规范表示。一个字符串池,最初是空的,由 String 类私下维护。当调用 intern 方法时,如果池中已经包含一个等于该 String 对象的字符串,该字符串由 equals(Object) 方法确定,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对该 String 对象的引用。因此,对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为真时,s.intern() == t.intern() 才为真。

所有的文字字符串和字符串值的常量表达式都是实习的。字符串文字在 Java™ 语言规范的第 3.10.5 节中定义。

返回: 与此字符串具有相同内容的字符串,但保证来自唯一字符串池。

希望对你有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-05
    • 2021-04-10
    • 1970-01-01
    • 2019-08-03
    • 2011-11-07
    • 2015-01-06
    • 1970-01-01
    • 2014-05-19
    相关资源
    最近更新 更多