【问题标题】:Questions about Java's String pool [duplicate]关于 Java 的字符串池的问题 [重复]
【发布时间】:2010-12-25 07:30:10
【问题描述】:

考虑这段代码:

String first = "abc"; 
String second = new String("abc");

当使用new 关键字时,Java 会再次创建abc String 对吗? 这将存储在常规堆还是String 池中? Strings 将在String 池中结束?

【问题讨论】:

标签: java string string-pool


【解决方案1】:

如果您使用new 关键字,则会创建一个新的String 对象。请注意,对象始终位于堆上——字符串池不是与堆分开的单独内存区域。

字符串池就像一个缓存。如果你这样做:

String s = "abc";
String p = "abc";

那么 Java 编译器足够聪明,可以只生成一个 String 对象,而 s 和 p 都将引用同一个 String 对象。如果你这样做:

String s = new String("abc");

那么池中就会有一个String对象,那个代表字面量"abc"的对象,另外还会有一个单独的String对象,不在池中,它包含一个内容的副本池化的对象。由于String 在Java 中是不可变的,因此您不会通过这样做获得任何收益;在 Java 中调用 new String("literal") 毫无意义,而且不必要地低效。

请注意,您可以在 String 对象上调用 intern()。这会将String 对象放入池中(如果它尚不存在),并返回对池化字符串的引用。 (如果它已经在池中,它只返回对已经存在的对象的引用)。有关详细信息,请参阅该方法的 API 文档。

另见String interning(维基百科)。

【讨论】:

  • 大概这是基于享元设计模式。
  • @Wim:是的,这本质上是享元模式。
  • intern() 池总是在堆上;我想问题是关于 类常量池
  • @Jesper:我想明确一点。 new String("literal") 正如你所说的那样没有意义,但我希望你不是在暗示调用“new String(someString)”没有意义并且不必要地低效。想象一下你有String s = "someExtremelyExtremelyLongStringLiteral";。然后你会想要使用String sub = new String(s.substring(0,1)); 而不是String sub = s.substring(0,1);,因为后一个版本会浪费内存。原因是当引用 s 消失时,仍然会有引用 sub 指向巨大的底层 char[] 所以它不能被 GC'ed
  • @Tom Java 7 更新 6 中更改了实现细节。现在,当您调用 "someExtremelyExtremelyLongStringLiteral".substring(0, 1) 时,将分配一个新的 1 字符 char[],并且子字符串完全独立于原文字面。在String、StringBuilder 等之间不再有任何char[] 共享。所以,往前走,真的没有很好的理由使用new String(String)
【解决方案2】:

在字节码中,第一个赋值是:

代码: 0:最不发达国家#2; //字符串abc 2:astore_1

而第二个是:

3:新#3; //类java/lang/String 6:重复 7:最不发达国家#2; //字符串abc 9:调用特殊#4; //方法java/lang/String."":(Ljava/lang/String;)V

所以第一个在池中(位置 #2),而第二个将存储在堆中。

编辑

由于CONSTANT_String_info store the index as U2(16 位,无符号),池最多可以包含2**16 = 65535 引用。如果你关心here more limits of the JVM。

【讨论】:

【解决方案3】:

每次你的代码创建一个字符串字面量

例如:

String str="Hello"; (string literal) 

JVM 首先检查字符串文字池。如果该字符串已存在于池中,则返回对池实例的引用。如果池中不存在字符串,则实例化一个新的 String 对象,然后将其放入池中。 Java 可以进行这种优化,因为字符串是不可变的,并且可以共享而不必担心数据损坏

【讨论】:

【解决方案4】:
String strObject = new String("Java");

和

String strLiteral = "Java";

这两个表达式都为您提供 String 对象,但它们之间存在细微差别。当您使用 new() 运算符创建 String 对象时,它总是在堆内存中创建一个新对象。另一方面,如果您使用字符串文字语法创建对象,例如“Java”,如果它已经存在,它可能会从字符串池(Perm gen 空间中的字符串对象缓存,现在在最近的 Java 版本中移动到堆空间)中返回一个现有对象。

【讨论】:

  • 您已从(Java 中字符串文字和新字符串对象之间的差异)[java67.blogspot.com/2014/08/…] 中获取 avoe 文本,最好提及您从中获取详细信息的参考 url
  • 它将从字符串池中返回一个对象,如果它是一个字面量的话。没有“可能”。
【解决方案5】:

你应该使用 new String(foo) 的唯一情况是当你想打破 == 时,这是一种奇怪的情况,或者当 foo 是一个更大的字符串的子字符串时,它具有有限的生命周期,例如

String mystring;
{
   String source = getSomeHeinouslyLargeString();
   mystring = new String(source.substring(1,3));
}

【讨论】:

    【解决方案6】:

    虽然晚了,但可能对仍然遇到此问题的人有用:

    String first = "abc";
    //One instance object in pool created. Instance variable “first” refers/points to pooled object
    
    String second = new String("abc");    
    //One instance object in heap created. Object in pool creation step will be skipped on account of first statement.
    

    所以总共将创建 2 个实例对象。一个在池中,另一个在堆中

    详细说明

    字符串优先 = "abc";

    这里是在池中创建的内容为“abc”的字符串对象。实例变量“first”将指向内容为“abc”的池对象。

    String second = new String("abc");

    这里将在堆中创建另一个内容为“abc”的字符串对象。实例变量“second”将指向内容为“abc”的堆对象。由于第一条语句,将跳过在池中创建内容为“abc”的字符串对象。原因如下。

    原因

    如果假设先前的语句(String first = "abc";)不存在相同的内容,那么通常使用“new”关键字,将创建2个字符串对象,一个在堆中(池外),另一个在池中(堆的子集区域)。 此外,实例变量“second”应该只指向堆对象,而不管对象是否在池中。

    现在由于存在与 new String("abc") 中内容相同的先前语句 (String first = "abc";),池中仅保留一个对象(内容为 "abc")。 因此,由于第一条语句,第二条语句将只创建 1 个对象而不是 2 个,并且该对象在堆中。将跳过创建池对象。

    //Additional Test on the concept
    System.out.println(first==second);  //returns false. Because first points to pool object while second points to heap object. And both objects are different (on account of different memory locations).
    
    second = second.intern();           //After interning, second now points to pool object. Note: intern is used so that reference variable points to pool area object and not heap object. Clearly it is applicable when we use new keyword.
    
    System.out.println(first==second);  //returns true. Because now both first and second objects now points to same pool object.
    

    【讨论】:

    • 如果String s = new String("first"); 是第一个语句,它将创建2 个对象(一个在堆中,一个在字符串池中),对吗?但是为什么它永远不会将自己与池中的对象进行比较时,它会在池中创建对象呢?
    【解决方案7】:
    String first = "abc"; 
    String second = new String("abc");
    

    在第一种情况下,池中只会创建一个对象。 在第二种情况下,两个对象将在池中创建一个(如果之前在池中不存在),在堆中创建一个。

    当您使用双引号 ex: "abc" 传递任何值时,您正在池中创建一个对象并将其传递给字符串构造函数,以便在堆中创建一个具有相同值的新对象。

    如果你看到了字符串构造函数,你可以看到它接受一个字符串。那根弦是什么?在创建之前,那个字符串对象是什么。它只不过是一个存储在字符串常量池中的对象。

    【讨论】:

      猜你喜欢
      • 2013-06-08
      • 1970-01-01
      • 2011-07-24
      • 1970-01-01
      • 2014-04-12
      • 2019-09-01
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多