常量池

Java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,不同于使用new关键字创建的对象所在的堆空间。

常量池项共分为11种类型:

常量池项类型  值  说明 
CONSTANT_Utf8  UTF-8编码的Unicode字符串 
CONSTANT_Integer  int型常量 
CONSTANT_Float  Float型常量 
CONSTANT_Long  Long型常量 
CONSTANT_Double double型常量 
CONSTANT_Class   对一个class的符号引用 
CONSTANT_String  String型常量 
CONSTANT_Fieldref  对一个字段的符号引用  
CONSTANT_Methodref  10  对一个类方法的符号引用 
CONSTANT_InterfaceMedthodref  11  对一个接口方法的符号引用 
CONSTANT_NameAndType 12  对名称和类型的符号引用 

例子1:常量池中对象和堆中的对象

 1 Integer i1 = new Integer(1);
 2 Integer i2 = new Integer(1);
 3 // i1,i2分别位于堆中不同的内存空间
 4 System.out.println(i1 == i2);// 输出false
 5  
 6 Integer i3 = 1;
 7 Integer i4 = 1;
 8 // i3,i4指向常量池中同一个内存空间
 9 System.out.println(i3 == i4);// 输出true
10  
11 // 很显然,i1,i3位于不同的内存空间
12 System.out.println(i1 == i3);// 输出false

例子2:

Java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character 这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。

 1 // 5种整形的包装类Byte,Short,Integer,Long,Character的对象,
 2 // 在值小于127时可以使用常量池
 3 Integer i1 = 127;
 4 Integer i2 = 127;
 5 System.out.println(i1 == i2);// 输出true
 6  
 7 // 值大于127时,不会从常量池中取对象
 8 Integer i3 = 128;
 9 Integer i4 = 128;
10 System.out.println(i3 == i4);// 输出false
11  
12 // Boolean类也实现了常量池技术
13 Boolean bool1 = true;
14 Boolean bool2 = true;
15 System.out.println(bool1 == bool2);// 输出true
16  
17 // 浮点类型的包装类没有实现常量池技术
18 Double d1 = 1.0;
19 Double d2 = 1.0;
20 System.out.println(d1 == d2);// 输出false

例子3:同样的,String也实现了常量池技术

1 // s1,s2分别位于堆中不同空间
2 String str1 = new String("hello");
3 String str2 = new String("hello");
4 System.out.println(str1 == str2);// 输出false
5  
6 // s3,s4位于池中同一空间
7 String str3 = "hello";
8 String str4 = "hello";
9 System.out.println(str3 == str4);// 输出true

例子4:关于Integer类的valueOf方法的一个小问题

1 //代码段A
2 Integer a1 = 127;
3 Integer b1 = 127;
4 System.out.println(a1 == b1);//输出true
5 //代码段B
6 Integer a2 = 128;
7 Integer b2 = 128;
8 System.out.println(a2 == b2);//输出false

原因:当你直接给一个Integer对象一个int值的时候,实际上却自动调用了valueOf方法,然而valueOf方法的内部实现是这样的:

1 public static Integer valueOf(int i) {
2     final int offset = 128;
3     if (i >= -128 && i <= 127) {
4         return IntegerCache.cache[i + offset];
5     }
6     return new Integer(i);
7 }

因为代码段B里赋予的值128溢出了,没有进入if (i >= -128 && i <= 127)条件语句,返回IntegerCache 类的静态数组的数据,IntegerCache类的内部实现是这样的:

1 private static class IntegerCache {
2     private IntegerCache() {
3     }
4     static final Integer cache[] = new Integer[-(-128) + 127 + 1];
5      
6     static {
7         for (int i = 0; i < cache.length; i++)
8             cache[i] = new Integer(i - 128);
9     }

而是直接执行了return new Integer(i)语句,又重新new了一个Integer对象出来,即代码段B实际上就相当于:

1 Integer a2 = new Integer(128);
2 Integer b2 = new Integer(128);

补充说明:关于上文提到的IntegerCache类,由于cache[]在IntegerCache类中是静态数组,也就是只需要初始化一次,即static{……}部分,所以,如果Integer对象初始化时是-128~127的范围,就不需要再重新定义申请空间,都是同一个对象—在IntegerCache.cache中,这样可以在一定程度上提高效率。


例子5:关于String类的intern方法

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

它遵循以下规则:对于任意两个字符串s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern()才为true。

例如:

 1 String str1 = new String("abc");
 2 String str2 = str1.intern();
 3 if (str1 == str2) {
 4     System.out.println("字符串abc在常量池中");
 5 } else {
 6     System.out.println("字符串abc不在常量池中");
 7 }
 8  
 9 String str3 = "abc";
10 String str4 = str3.intern();
11 if (str3 == str4) {
12     System.out.println("字符串abc在常量池中");
13 } else {
14     System.out.println("字符串abc不在常量池中");
15 }

还有,

1 String str1 = "abc";
2 String str2 = new String("abc");
3 String str3 = new String("abc");
4 str2.intern();// 不起作用的代码
5 str3 = str3.intern();// 起作用
6 System.out.println(str1 == str2);// 输出:false
7 System.out.println(str1 == str2.intern());// 输出:true
8 System.out.println(str1 == str3);// 输出:true

 

posted @ 2015-02-15 13:44  咸蛋oye  阅读(192)  评论(0编辑  收藏  举报