关于Java的问题';s弦池

关于Java的问题';s弦池,java,string,string-pool,Java,String,String Pool,考虑以下代码: String first = "abc"; String second = new String("abc"); 使用new关键字时,Java将再次创建abc字符串,对吗? 这将存储在常规堆还是String池中? 有多少Strings将在String池中结束?如果使用new关键字,将创建一个新的String对象。请注意,对象始终位于堆上-字符串池不是与堆分离的单独内存区域 字符串池就像一个缓存。如果您这样做: String s = "abc"; String p = "abc

考虑以下代码:

String first = "abc"; 
String second = new String("abc");
使用
new
关键字时,Java将再次创建
abc
字符串,对吗?
这将存储在常规堆还是
String
池中?
有多少
String
s将在
String
池中结束?

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

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

String s = "abc";
String p = "abc";
String s = new String("abc");
然后Java编译器足够聪明,只生成一个
String
对象,
s
p
都将引用同一个String对象。如果您这样做:

String s = "abc";
String p = "abc";
String s = new String("abc");
然后,池中会有一个
String
对象,该对象表示文本
“abc”
,并且会有一个单独的
String
对象,而不是池中的对象,该对象包含池中对象内容的副本。因为
String
在Java中是不可变的,所以这样做并没有什么好处;调用
新字符串(“literal”)
在Java中毫无意义,而且效率低下

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


另请参见(维基百科)。

字节码中,第一个任务是:

Code: 0: ldc #2; //String abc 2: astore_1 代码: 0:ldc#2//字符串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 3:新的#3//类java/lang/String 6:dup 7:最不发达国家2//字符串abc 9:特别是#4//方法java/lang/String。“”:(Ljava/lang/String;)V 因此,第一个存储在池中(位置#2),而第二个存储在堆中

编辑


由于
常量字符串
(16位,无符号),池最多可以包含
2**16
=
65535
引用。在您关心的情况下。

每次代码创建字符串文字时

例如:

String str="Hello"; (string literal) 

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

唯一应该使用新字符串(foo)的时间是当您想要中断==,这是一种奇怪的情况,或者当foo是具有有限生存期的更大字符串的子字符串时,例如

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


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

虽然很晚了,但对于仍然遇到以下问题的人来说可能很有用:

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个实例对象。一个在池中,另一个在堆中

详细说明

String first=“abc”

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

第二个字符串=新字符串(“abc”)

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

原因

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

现在,由于前面的语句(stringfirst=“abc”;)的内容与新字符串(“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.
在第一种情况下,只有一个对象将在池中创建。 在第二种情况下,两个对象将在池中创建一个对象(如果池中以前不存在此对象),在堆中创建一个对象

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


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

大概这是基于flyweight设计模式。@Wim:是的,这本质上就是flyweight模式。intern()池始终在堆上;我想问题是关于类常量的pool@Jesper当前位置我想澄清一些事情
newstring(“literal”)
不像您所说的那样有意义,但我希望您不是在暗示调用“newstring(someString)”没有意义,而且效率低下。假设您有
String s=“someextremelyextremelyngthringliteral”。然后您需要使用
stringsub=newstring(s.substring(0,1))而不是
Stri
String first = "abc"; 
String second = new String("abc");