关于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");