内容原创,欢迎指正,转载注明http://slevin1994.iteye.com/blog/2415778
字面常量
《Java语言规范 基于Java SE 8》3.10节 写道字面常量是类型为简单类型,String类型和空类型的**值**在源程序中的表示。包括整数字面常量,浮点数字面常量,布尔字面常量,字符字面常量,字符串字面常量和空字面常量。
- 整数字面常量,它的类型是long或者int(基本类型)
- 浮点数字面常量,它的类型是float或者double(基本类型)
- 布尔字面常量,它的类型是boolean(基本类型)
- 字符字面常量,它的类型是char(基本类型)
- 字符串字面常量,它的类型是String,是对String类的实例的引用(引用类型)
- 空字面常量,总是空类型
《Java语言规范 基于Java SE 8》3.10.5节 写道而且,一个字符串字面常量总是引用String类的同一个实例。这是因为字符串字面常量,或者更一般的情况,表示常量
表达式的值的字符串,被通过使用String.intern方法而“限定”了,这样做是为了让它们可以共享唯一的实例。
综上,相同的字符串字面常量总是引用同一个String实例,这个实例被String.intern方法而“限定”了。
==================================================================================
关于class文件常量池的理解
class文件中的常量池存放的是class文件结构及其子结构中所有的字面常量、类或
接口名、字段和方法等。比如对于类中的一个属性的定义语句:
private String name = new String("slevin");
属性“name”及其内容“slevin”都保存在class文件的常量池中
- 属性名“name”以CONSTANT_Fieldref_info格式存储
- 内容“slevin”以CONSTANT_String_info格式存储的
注意:虽然“slevin”是String字面常量,但目前
还不是引用类型:
《Java虚拟机规范SE8》4.4.3节 写道> CONSTANT_String_info格式包含一个该格式对应的tag,和一个指向CONSTANT_Uft8_info的索引。这个结构表示Unicode码点序列,这个序列
最终会初始化成一个String对象。
什么时候会“初始化成一个String对象”呢?这就涉及到运行时常量池了。
==================================================================================
关于运行时常量池的理解:
《Java虚拟机规范SE8》5.1节 写道当类或接口创建时,它的
二进制表示中的常量池表(即class文件常量池)被用来构造运行时常量池。运行时常量池最初阶段一部分是符号引用,另一部分得自常量池表中的某些项。
《Java虚拟机规范SE8》5.1节 写道为了得到字符常量,Java虚拟机需要检查CONSTANT_String_info结构中的码点序列:
- 如果某String实例所包含的Unicode码点序列与CONSTANT_String_info结构所给出的序列相同,而之前又曾在该实例上面调用过String.intern方法,那么此次字符常量获取的结果将是一个指向相同String实例的引用
- 否则,会创建一个新的String实例,其中包含由CONSTANT_String_info结构所给出的Unicode码点序列;字符常量获取的结果是指向那个新Strlng实例的引用。最后,新String实例的intern方法被Java虚拟机自动调用
再看以下JDK8中关于String.intern()方法的描述:
JDK8 写道public String intern()
Returns a
canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked,
if the pool already contains a string equal to this String object as determined by the equals(Object) method,
then the string from the pool is returned.
Otherwise, this String object is
added 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.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java? Language Specification.
Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.
所以可以这样理解:
- String字面量的值先在编译期间被记录在class文件中的常量池表中
- 类被创建时,运行时常量池根据class文件常量池表中的内容,创建String字面量
- String字面量是对String类实例的引用,这些实例是被intern过的,存放在堆中;字面量存放在运行时常量池中
- 堆中所有intern过的String实例都通过String类管理,所以可以说String池是由String类管理的。
==================================================================================
关于String池的理解:
String池是由String类维护的,位于堆中。对String池中的String实例的引用被存放在运行时常量池中。如果方法中有如下代码:
String name1="slevin";
name1得到的是String池中的一个String对象的引用,该对象的值也是“slevin”
String name2=new String("slevin").intern();
name2得到的是String池中的一个String对象的引用,该对象的值也是“slevin”
我个人认为:
- name1直接通过运行时常量池得到了池中的“slevin”实例的引用,因为编译时这行代码的操作应该是根据class常量池中该字面量的索引得到值并放入局部变量表。而类在被创建后,class常量池被解析成运行时常量池。
- name2通过String类维护的String池得到池中的“slevin”实例的引用,尽管这个引用已经放在了运行时常量池中,因为编译时这行代码的操作应该是根据class常量池中该字面量的索引得到值,然后当做参数传给String.intern()方法。
以上两条是我的推测,以后会根据反汇编加以证实。