常量池(Runtime Constant Poo)
常量池Java中可以分为三种:字符串常量池(堆)、Class文件常量池、运行时常量池(堆)。
1.字符串常量池(String Pool)
- 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
- 字符串常量池是所有类公用的一块空间,在一个虚拟机中只有一块常量池区域。
- 字符串常量池存在于堆上。
- 用来存储一些全局的字符串对象的引用。
- 在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到字符串常量池中。
2.Class文件常量池(Class Constant Pool)
- .java文件在通过javac编译后会生成.class文件。
- Class文件中存放类的版本、字段、方法、接口等描述信息外,还有一项是常量池。
- 常量池用于存放编译期间生产的各种字面量和符号引用。
- 每个类都有一个Class文件常量池。
3.运行时常量池(Runtime Constant Pool)
- JDK1.7及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。
- 在类加载后将存Class文件常量池的内容加载到运行时常量池。
- 每个类都有一个运行时常量池。
- 当常量池无法再申请到内存时会抛出OutMemoryError异常。
4.举例 - 基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。
- 两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。
Double d3 = 1.2;
Double d4 = 1.2;
System.out.println(d3 == d4); // false
Integer i1= 40; // Java在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。
Integer i2 = new Integer(40); // 这种情况下会创建新的对象,所以尽量避免使用这种方式。
System.out.println(i1 == i2); // false
String str1 = "abcd"; // 直接使用双引号声明出来的String对象会直接存储在常量池中
String str2 = new String("abcd"); // 堆内存空间创建一个新的对象,str2指向堆对象
String str3 = str2.intern(); // intern()如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用。
String str4 = str1.intern();
System.out.println(str1 == str2); // false
System.out.println(str2 == str3); // false
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // false
System.out.println(str2 == str4); // false
System.out.println(str3 == str4); // true
/**
* String str2 = new String("abcd"); 创建了两个对象
* 字符串常量在编译期确定放入常量池,堆上的对象在运行期初始化阶段确定
*/
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing"; //常量池中的对象
String str4 = str1 + str2; //在堆上创建的新的对象
String str5 = "string"; //常量池中的对象
System.out.println(str3 == str4); // false
System.out.println(str3 == str5); // true
Integer i1 = 40;// Java在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。
Integer i2 = new Integer(40); // 创建新的对象,所以尽量避免使用这种方式。
Integer i3 = new Integer(40);
Integer i4 = new Integer(0);
System.out.println(i2 == i3); // false
System.out.println(i2 == i3 + i4); // true,Integer对象不适用 + 操作符,会先进行自动拆箱操作,再进行数值相加。Integer对象无法与数值进行直接比较,所以i2自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。
System.out.println(40 == i3 + i4); // true
参考:
http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/