Integer常量池

Java中有8中基本数据类型,基本类型是分配在栈空间上的,Java为我们提供了分配在堆栈空间的8种基本类型的包装类(ByteShortIntegerLongFloatDoubleBooleanCharacter),这些包装类的实例对象除了包装的数据不一样外,其他的没有很明显的差别。

 就以Integer为例,各个Integer对象的差别在于包装的int类型的值不同而已,假设Java中的Integer类只是简单的对int值进行包装,这样会出现什么问题呢?若程序中需要使用大量包装了相同int值的Integer对象,这种情况会对内存造成极大浪费,没有人会忍受相同的对象在自己的程序中出现成千上万次,毕竟轮子发明一次就行了,对于没有什么外部状态的Integer对象,包装了相同int值的Integer对象在程序中出现一次就行。

 因此sun公司对Integer做了特殊的处理,对Integer类应用了享元模式,享元模式它的优点在于,通过共享一些对象降低内存中相同对象的数量。使用了设计模式固然会对程序带来诸多好处,但它也会是程序付出一些代价。想象一下int类型从0x800000000x7fffffff如果把所有这些数字包装成共享对象,那需要的对象的数量将是多么的庞大,恐怕还不如不进行处理呢,另外在程序中有很多用不上(在项目中从来那没见那个程序会把所有的int值用个遍的)。因此Integer只对包装了int值在-128-127这个范围的Integer对象做了共,从下面这段代码可以的运行结果可以看出。

程序遍历了从0x800000000x7fffffff所有的int类型的值,并把每一个数组封装成了两个对象,如果这两个对象属于同一个对象就打印出来,

public class Test1 {
 public static void main(String[] args) {
  for (int i = Integer.MIN_VALUE;i < Integer.MAX_VALUE;i++) {
   Integer i1 = i;
   Integer i2 = i;
   if (i1 == i2)
    System.out.print(i + " ");
  }
 }
}

打印的结果是:-128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113 -112 -111 -110 -109 -108 -107 -106 -105 -104 -103 -102 -101 -100 -99 -98 -97 -96 -95 -94 -93 -92 -91 -90 -89 -88 -87 -86 -85 -84 -83 -82 -81 -80 -79 -78 -77 -76 -75 -74 -73 -72 -71 -70 -69 -68 -67 -66 -65 -64 -63 -62 -61 -60 -59 -58 -57 -56 -55 -54 -53 -52 -51 -50 -49 -48 -47 -46 -45 -44 -43 -42 -41 -40 -39 -38 -37 -36 -35 -34 -33 -32 -31 -30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 

也就是说sun只对一个字节内的数值的Integer对象做了共享。

 

sunInteger类怎么样进行处理的呢,我们做个假设。。。我能想到的有两种方式

 第一:Integer类中有一个缓存对象的Mapkey是数值,value为对象的Integer对象,当使用一个数字number实例化Integer对象时,判断一下数字的值,若number >= -128 && number <=  127时装进map里面,下次再需要实例化数值范围在-128127之间的Integer对象时,先看看map里面有没有,有则返回对应对象,没有则实例化并放进map(这种方式,程序运行效率的相对低,但是内存占用相对低)

 第二:直接搞出来-128127的对象缓存起来,放在map中,当实例化-128127之间的Integer对象时,直接拿出缓存中的对应的对象(这种方式付出的代价,就是包装了-128127这些Integer对象占用的内存),到底使用了那种方式,用程序是判断不出来的,就翻开java.lang.Integer的源代码,我用EditPlus打开了这个类,翻了老半天找到了在Integer类中有一个私有内部类IntegerCache

 private static class IntegerCache {
        static final int high;
        static final Integer cache[];

        static {
            final int low = -128;

            // high value may be configured by property
            int h = 127;
            if (integerCacheHighPropValue != null) {
                // Use Long.decode here to avoid invoking methods that
                // require Integer's autoboxing cache to be initialized
                int i = Long.decode(integerCacheHighPropValue).intValue();
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - -low);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

这个类的第三行处有个名字叫cacheInteger数组,相当于我们假想的map,数组的长度为(high - low) + 1,就是一个字节内,highcache值的下限-128lowcache值的上限127


 for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

上面这段代码就是直接new出来-128127255个对象缓存起来,做成一个常量池,在看看Integer中有一个通过一个int值拿到对应的Integer对象的静态方法valueOf

 public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }

里面的实现就是先判断一下数字是不是在-128127之间,是的话直接返回cache中对应的Integer对象,否而new出来对应对象并返回,呵呵,怎么样,假想2成立,为什么使用第二种方式呢,思考了下,现在的硬件配置越来越高,内存越来越大,这一点内存相对之下是非常微小的,在空间与效率之间,java编写api的人员选择了效率高占用空间的方式,有点著名的加速查找的hash算法的意思哦。

你可能感兴趣的:(黑马,integer,cache,java,character,sun)