常量缓存池浅析

来源:互联网 发布:义乌美工学徒招聘 编辑:程序博客网 时间:2024/06/15 22:33
我们先看一段代码,很简单对不对

public class Demo {     public static void main(String[] args) {           Integer i1 = 100;           Integer i2 = 100;           System.out.println(i1 == i2);           Integer i3 = 1000;           Integer i4 = 1000;           System.out.println(i3 == i4);     }}


执行一下:
     
和大多数人心里想的不一样吧,好多人还在纠结,到底全是true还是全是false。

然而一个是true,一个是false,这是为什么呢?

首先我们找到.class文件来反编译一下,根据Java编译机制, .java文件在编译后会生成 .clas文件给JVM加载执行

public class Demo {     public static void main(String[] args) {           Integer i1 = Integer.valueOf(100);           Integer i2 = Integer.valueOf(100);           System.out.println(i1 == i2);
           Integer i3 = Integer.valueOf(1000);           Integer i4 = Integer.valueOf(1000);           System.out.println(i3 == i4);     }
}


诶,发现编译器在编译我们的代码时,在声明的变量上加了valueOf方法,那进一步来看一下valueOf方法的实现

    public static IntegervalueOf(inti) {
        if (i >= IntegerCache.low &&i <= IntegerCache.high)         看一下是否在缓存的数组里,如果在,直接取
            return IntegerCache.cache[i + (-IntegerCache.low)];      数组里相应的对象返回
        return new Integer(i);                                        不在缓存的数组里,直接new一个对象返回
    }

我们发现,Integer的作者在写这个类时,为避免重复创建对象,对Integer值做了缓存,如果这个值在缓存范围内,直接返回缓存好的对象,否则new一个新的对象返回,那究竟这个缓存到底缓存了什么呢?看一下IntegerCsche这个类:
   privatestaticclassIntegerCache {
       staticfinalintlow = -128;
       staticfinalinthigh;
       staticfinal Integercache[];
       static {
            // high value may be configured by property
            /*
            *  检查虚拟机里有没有相应的配置,如果有,取该值;如果没有,取默认的127
            */
           inth = 127;
            StringintegerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
           if (integerCacheHighPropValue !=null) {
               try {
                   inti =parseInt(integerCacheHighPropValue);
                   i = Math.max(i, 127);
                   // Maximum array size is Integer.MAX_VALUE
                   h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                }catch( NumberFormatExceptionnfe) {
                   // If the property cannot be parsed into an int, ignore it.
                }
            }
           high =h;
                           
            /*
            *  创建缓存数组,并给数组初始化值(缓存值)
            */
           cache =new Integer[(high -low) + 1];
           intj =low;
           for(intk = 0; k < cache.length;k++)
               cache[k] =new Integer(j++);
           // range [-128, 127] must be interned (JLS7 5.1.7)
           assertIntegerCache.high >= 127;
        }
       private IntegerCache() {}
    }


这是一个内部静态类,该类只能在Integer这个类的内部访问,这个类在初始化的时候,会去加载JVM的配置,如果有值,就用配置的值初始化缓存数组,否则就缓存-128到127之间的值。

再来看看之前的代码:



结论:
我们在比较两个Integer对象时,无论是怎么声明的,都一定要使用equals去比较,不能用==。



引申:
8种基本类型的包装类和对象池

java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。以下是一些对应的测试代码:
publicclassDemo {
     publicstaticvoidmain(String[]args) {
           // 5种整形的包装类Byte,Short,Integer,Long,Character的对象,
           // 在值小于127时可以使用常量池
           Short s1= 127;
           Shorts2= 127;
           System.out.println(s1==s2);// 输出true
           // 值大于127时,不会从常量池中取对象
           Shorts3= 128;
           Shorts4= 128;
           System.out.println(s3==s4);// 输出false

           // Character
           Characterc1= 127;
           Characterc2= 127;
           System.out.println(c1==c2);// 输出true
           // 值大于127时,不会从常量池中取对象
           Characterc3= 128;
           Characterc4= 128;
           System.out.println(c3==c4);// 输出false
                      
           // Boolean类也实现了常量池技术
           Booleanbool1=true;
           Booleanbool2=true;
           System.out.println(bool1==bool2);// 输出true
           // 浮点类型的包装类没有实现常量池技术
           Doubled1= 1.0;
           Doubled2= 1.0;
           System.out.println(d1==d2);// 输出false
     }
}

看一下Console
原创粉丝点击