Java的String.intern()的目的是什么?

我知道有两种方法可以在Java中创建String:

String a = "aaa"; String b = new String("bbb"); 

第一种方式,Java肯定会在字符串池中创建一个String对象并引用它。 (假设“aaa”之前不在游泳池里。)

使用第二种方法,将在堆中创建一个对象,但是jvm还会在字符串池中创建一个对象吗?

在这篇关于Java字符串池的问题中 ,@ Jesper说:

如果你这样做:

 String s = new String("abc"); 

然后池中将有一个String对象,一个代表文字“abc”,>的对象,并且将有一个单独的String对象,而不是在池中,它包含池对象的>内容的副本。

如果那是真的,那么每次都使用new String("bbb"); ,在池中创建一个对象“bbb”,这意味着通过上面的任何一种方式,java将始终在池中创建一个字符串对象。 那么intern()用于什么? 在文档http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern()中 ,它说:

调用实习方法时,如果池已经包含等于此字符串对象的字符串(由equals(Object)方法确定),则返回池中的字符串。 否则,将此String对象添加到池中,并返回对此String对象的引用。

这意味着有些字符串不在池中,这可能吗? 哪一个是真的?

String对象可以通过两种方式进入池:

  • 在像"bbb"这样的源代码中使用文字。
  • 使用intern

intern用于当你有一个不是来自池的字符串时。 例如:

 String bb = "bbb".substring(1); // substring creates a new object System.out.println(bb == "bb"); // false System.out.println(bb.intern() == "bb"); // true 

或略有不同:

 System.out.println(new String("bbb").intern() == "bbb"); // true 

new String("bbb")会创建两个对象……

 String fromLiteral = "bbb"; // in pool String fromNewString = new String(fromLiteral); // not in pool 

……但它更像是一个特例。 它创建了两个对象,因为"bbb"指的是一个对象 :

字符串文字是对String […]类实例的引用。

此外,字符串文字始终引用类String的相同实例。

new String(...)创建它的副本。

但是,有很多方法可以在不使用文字的情况下创建String对象。 脱离我的头顶:

  • 所有执行某种突变的String方法。 (子串,拆分,替换等)
  • 从某种输入(例如扫描仪或读取器)读取字符串。
  • 当至少一个操作数不是编译时常量时连接。

intern允许您将它们添加到池中或检索现有对象(如果有)。 在大多数情况下,实习字符串是不必要的,但它可以用作优化,因为:

  • 它可以让你与==进行比较。
  • 它可以节省内存,因为重复可以被垃圾收集。

如你所知,Java编程语言中的不可变对象中的String ,这意味着一旦构造就无法改变。 因此,JVM能够维护文字池,这有助于减少内存使用并提高性能。 每次使用String文字时,JVM都会检查文字池,如果文字已经可用,则会返回相同的引用。 如果不是,则会在文字池中创建并添加新的String对象。

当您尝试创建类似基元或文字/常量的String时,将应用此理论。

 String str = "bbb"; 

但是当你创建一个新的String对象时

 String str = new String("bbb")); 

覆盖上述规则并始终创建新实例。

但即使您使用new运算符创建StringString类中的intern API也可用于从literal池中选择String引用。 请查看下面给出的示例。 虽然str3是使用new运算符创建的,因为我们使用了intern方法,JVM从literal池中获取了引用。

 public class StringInternExample { public static void main(final String args[]) { final String str = "bbb"; final String str1 = "bbb"; final String str2 = new String("bbb"); final String str3 = new String("bbb").intern(); System.out.println("str == str1 : "+(str == str1)); System.out.println("str == str2 : "+(str == str2)); System.out.println("str == str3 : "+(str == str3)); } } 

上述代码的输出:

 str == str1 : true str == str2 : false str == str3 : true 

你可以看一下: 对字符串不变性的困惑

答案来源: http : //ourownjava.com/java/java-string-immutability-and-intern-method/

Shishir

是的, new String("abc")将在内存中创建一个新对象,因此建议避免使用它。 请查看Josh Bloch的Effective Java第5项“避免创建不必要的对象”,其中更好地解释:

作为不该做的极端例子,请考虑以下声明:

String s = new String("stringette"); // DON'T DO THIS!

该语句在每次执行时都会创建一个新的String实例,并且这些对象创建都不是必需的。 String构造函数(“stringette”)的参数本身就是一个String实例,在function上与构造函数创建的所有对象完全相同。 如果这种用法发生在循环或频繁调用的方法中,则可以不必要地创建数百万个String实例。 改进版只是以下内容:

String s = "stringette";

此版本使用单个String实例,而不是每次执行时都创建一个新实例。 此外,保证对象将被同一虚拟机中运行的任何其他代码重用,恰好包含相同的字符串文字[JLS,3.10.5]。

http://uet.vnu.edu.vn/~chauttm/e-books/java/Effective.Java.2nd.Edition.May.2008.3000th.Release.pdf

使用第二种方法,将在堆中创建一个对象,但是jvm还会在字符串池中创建一个对象吗?

是的, 它是字符串文字 "bbb" ,它确保了实习字符串1 。 字符串构造函数创建一个新的字符串对象,该对象是具有相同长度和内容的副本 – 新创建的字符串不会自动实现。

如果这是真的,那么每次使用新的String(“bbb”);时,在池中创建一个对象“bbb”,这意味着通过上面的任何一种方式,java将始终在池中创建一个字符串对象。 那么intern()用于什么?

只有字符串文字会自动实现。 必须手动实现其他字符串对象,如果这是所需的行为。

这意味着有些字符串不在池中,这可能吗?

除了手动调用String.intern只有字符串文字才会生成实习字符串。

虽然我建议对这种情况使用专门的集合,但是在可以用来避免创建额外的重复对象的情况下,实习可能很有用。 实习可能有益的一些用例 – 例如,相同的字符串值可以多次出现 – 在JSON密钥和XML元素/属性名称中。


1这是微不足道的原因,请考虑:

 String _b = "bbb"; // string from string literal (this is interned) String b = new String(_b); // create a NEW string via "copy constructor" b == _b // -> false (new did NOT return an interned string) b.equals(_b) // -> true (but it did return an equivalent string) b.intern() == _b // -> true (which interns to .. the same string object)