考虑以下代码:
String first = "abc";
String second = new String("abc");
使用new
关键字时,Java会再次创建abc
String
吗?
这会存储在常规堆还是String
池中?
String
池中将有多少String
个结束?
答案 0 :(得分:94)
如果您使用new
关键字,则会创建一个新的String
对象。请注意,对象始终位于堆上 - 字符串池不是与堆分开的单独内存区域。
字符串池就像一个缓存。如果你这样做:
String s = "abc";
String p = "abc";
然后Java编译器足够智能,只能生成一个String
对象,而s
和p
都将引用同一个String对象。如果你这样做:
String s = new String("abc");
然后池中会有一个String
对象,代表文字"abc"
的对象,并且会有一个单独的String
对象,而不是在池中,包含合并对象内容的副本。由于String
在Java中是不可变的,所以你不会通过这样做获得任何东西;调用new String("literal")
在Java中永远不会有意义,并且不必要地效率低下。
请注意,您可以在intern()
对象上调用String
。这将把String
对象放入池中(如果它尚不存在),并返回对池化字符串的引用。 (如果它已经在池中,它只返回对已存在的对象的引用)。有关详细信息,请参阅该方法的API文档。
另见String interning(维基百科)。
答案 1 :(得分:12)
在字节码中,第一个作业是:
Code: 0: ldc #2; //String abc 2: astore_1
而第二个是:
3: new #3; //class java/lang/String 6: dup 7: ldc #2; //String abc 9: invokespecial #4; //Method 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。
答案 2 :(得分:7)
每次代码创建字符串文字
例如:
String str="Hello"; (string literal)
JVM首先检查字符串文字池。如果池中已存在该字符串,则返回对池化实例的引用。如果池中不存在该字符串,则新的String对象将实例化,然后放入池中。 Java可以进行这种优化,因为字符串是不可变的,可以共享而不用担心数据损坏
答案 3 :(得分:3)
String strObject = new String("Java");
和
String strLiteral = "Java";
两个表达式都为您提供了String对象,但它们之间存在细微差别。使用new()运算符创建String对象时,它总是在堆内存中创建一个新对象。另一方面,如果使用String文字语法创建对象,例如" Java",它可以返回String池中的现有对象(Perm gen空间中的String对象的缓存,现在它已移动到最近Java版本中的堆空间),如果它已经存在。
答案 4 :(得分:2)
你应该使用new String(foo)的唯一时间是你想要破坏==,这是一个奇怪的情况,或者当foo是一个更大的字符串,具有有限的生命周期的子字符串,例如
String mystring;
{
String source = getSomeHeinouslyLargeString();
mystring = new String(source.substring(1,3));
}
答案 5 :(得分:0)
虽然很晚,但对于仍然遇到此问题的人可能会有用:
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&#34 ;;
这里是一个包含内容" abc"的字符串对象。在池中创建。实例变量“first”将指向内容为“abc”的池对象。
String second = new String(" abc");
这里是另一个带有内容" abc"的字符串对象。将在堆中创建。实例变量“second”将指向内容为“abc”的堆对象。包含内容" abc"的字符串对象由于第一个声明,将跳过池中的创建。原因如下。
<强>原因强>
如果假设先前语句(String first =&#34; abc&#34 ;;)不具有相同的内容,那么通常使用“new”关键字,将在堆(外部池)中创建一个2个字符串对象,池中的另一个(堆的子集区域)。 实例变量&#34; second&#34;应该只指向堆对象,而不管对象是否在池中。
现在由于先前语句(String first =&#34; abc&#34 ;;)的存在与新String中的内容相同(&#34; abc&#34;),只有一个对象(含有内容) &#34; abc&#34;)保留在池中。 因此,由于第一个语句,第二个语句将只创建一个对象而不是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.
答案 6 :(得分:0)
String first = "abc";
String second = new String("abc");
在第一种情况下,只有一个对象将在Pool中创建。 在第二种情况下,两个对象将在池中创建一个(如果以前在池中不存在),而在堆中创建一个。
当您使用双引号ex:“abc”传递任何值时,您将在池中创建一个对象并将其传递给字符串构造函数,以便在堆中创建具有相同值的新对象。
如果您看到字符串构造函数,则可以看到它接受字符串。那串是什么?在创建之前,该字符串对象是什么。它只是存储在String Constant池中的对象。