71

考虑这段代码:

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

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

4

7 回答 7

97

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

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

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

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

String s = new String("abc");

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

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

另见字符串实习(维基百科)。

于 2009-12-10T15:52:52.330 回答
12

bytecode中,第一个赋值是:

  代码:
   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 索引存储为 U2(16 位,无符号),因此池可以包含最大2**16=65535引用。如果您在这里关心JVM 的更多限制

于 2009-12-10T15:53:46.120 回答
7

每次您的代码创建一个字符串文字时

例如:

String str="Hello"; (string literal) 

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

于 2009-12-10T17:06:05.467 回答
3
String strObject = new String("Java");

String strLiteral = "Java";

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

于 2015-05-25T11:21:29.750 回答
2

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

String mystring;
{
   String source = getSomeHeinouslyLargeString();
   mystring = new String(source.substring(1,3));
}
于 2009-12-12T05:44:27.553 回答
1

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

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.
于 2017-12-14T07:36:55.513 回答
0
String first = "abc"; 
String second = new String("abc");

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

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

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

于 2018-02-22T10:51:08.117 回答