JVM运行时常量池与String池

内容原创,欢迎指正,转载注明http://slevin1994.iteye.com/blog/2415778

[b][size=large]字面常量[/size][/b]
[quote="《Java语言规范 基于Java SE 8》3.10节"]字面常量是类型为简单类型,String类型和空类型的**值**在源程序中的表示。包括整数字面常量,浮点数字面常量,布尔字面常量,字符字面常量,字符串字面常量和空字面常量。
[list]
[*]整数字面常量,它的类型是long或者int(基本类型)
[*]浮点数字面常量,它的类型是float或者double(基本类型)
[*]布尔字面常量,它的类型是boolean(基本类型)
[*]字符字面常量,它的类型是char(基本类型)
[*][b]字符串字面常量,它的类型是String,是对String类的实例的引用(引用类型)[/b]
[*]空字面常量,总是空类型[/list][/quote]

[quote="《Java语言规范 基于Java SE 8》3.10.5节"]而且,一个字符串字面常量总是引用String类的同一个实例。这是因为字符串字面常量,或者更一般的情况,表示常量表达式的值的字符串,被通过使用String.intern方法而“限定”了,这样做是为了让它们可以共享唯一的实例。[/quote]

[b]综上,相同的字符串字面常量总是引用同一个String实例,这个实例被String.intern方法而“限定”了。[/b]

==================================================================================

[b][size=large]关于class文件常量池的理解[/size][/b]
class文件中的常量池存放的是class文件结构及其子结构中所有的字面常量、类或接口名、字段和方法等。比如对于类中的一个属性的定义语句:

private String name = new String("slevin");


属性“name”及其内容“slevin”都保存在class文件的常量池中
[list]
[*]属性名“name”以CONSTANT_Fieldref_info格式存储
[*]内容“slevin”以CONSTANT_String_info格式存储的
[/list]
注意:虽然“slevin”是String字面常量,但目前[b]还不是引用类型[/b]:
[quote="《Java虚拟机规范SE8》4.4.3节"]> CONSTANT_String_info格式包含一个该格式对应的tag,和一个指向CONSTANT_Uft8_info的索引。这个结构表示Unicode码点序列,这个序列[b]最终会初始化成一个String对象[/b]。[/quote]
什么时候会“初始化成一个String对象”呢?这就涉及到运行时常量池了。

==================================================================================

[b][size=large]关于运行时常量池的理解:[/size][/b]
[quote="《Java虚拟机规范SE8》5.1节"]当类或接口创建时,它的二进制表示中的常量池表(即class文件常量池)被用来构造运行时常量池。运行时常量池最初阶段一部分是符号引用,另一部分得自常量池表中的某些项。[/quote]

[quote="《Java虚拟机规范SE8》5.1节"]为了得到字符常量,Java虚拟机需要检查CONSTANT_String_info结构中的码点序列:
[list]
[*]如果某String实例所包含的Unicode码点序列与CONSTANT_String_info结构所给出的序列相同,而之前又曾在该实例上面调用过String.intern方法,那么此次字符常量获取的结果将是一个指向相同String实例的引用
[*]否则,会创建一个新的String实例,其中包含由CONSTANT_String_info结构所给出的Unicode码点序列;字符常量获取的结果是指向那个新Strlng实例的引用。最后,新String实例的intern方法被Java虚拟机自动调用[/list][/quote]

再看以下JDK8中关于[url=https://docs.oracle.com/javase/8/docs/api/]String.intern()[/url]方法的描述:
[quote="JDK8"]public String intern()
Returns a canonical representation for the string object.
[b]A pool of strings, initially empty, is maintained privately by the class String[/b].
[b]When[/b] the intern method is invoked, [b]if[/b] the pool already contains a string equal to this String object as determined by the equals(Object) method, [b]then[/b] the string from the pool is returned. [b]Otherwise[/b], this String object is [b]added[/b] to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
[b]All literal strings and string-valued constant expressions are interned. [/b]String literals are defined in section 3.10.5 of the The Java™ Language Specification.
Returns:
[b]a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.[/b][/quote]

所以可以这样理解:

[b][list=1]
[*]String字面量的值先在编译期间被记录在class文件中的常量池表中
[*]类被创建时,运行时常量池根据class文件常量池表中的内容,创建String字面量
[*]String字面量是对String类实例的引用,这些实例是被intern过的,存放在堆中;字面量存放在运行时常量池中
[*]堆中所有intern过的String实例都通过String类管理,所以可以说String池是由String类管理的。
[/list][/b]

==================================================================================

[b][size=large]关于String池的理解:[/size][/b]
String池是由String类维护的,位于堆中。对String池中的String实例的引用被存放在运行时常量池中。如果方法中有如下代码:

String name1="slevin";

name1得到的是String池中的一个String对象的引用,该对象的值也是“slevin”

String name2=new String("slevin").intern();

name2得到的是String池中的一个String对象的引用,该对象的值也是“slevin”

[b]我个人认为:[/b]
[list]
[*]name1直接通过运行时常量池得到了池中的“slevin”实例的引用,因为编译时这行代码的操作应该是根据class常量池中该字面量的索引得到值并放入局部变量表。而类在被创建后,class常量池被解析成运行时常量池。
[*]name2通过String类维护的String池得到池中的“slevin”实例的引用,尽管这个引用已经放在了运行时常量池中,因为编译时这行代码的操作应该是根据class常量池中该字面量的索引得到值,然后当做参数传给String.intern()方法。
[/list]
[b]以上两条是我的推测,以后会根据反汇编加以证实。[/b]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值