深入理解Integer缓存机制

之前有对数据类型做介绍:

Java数据类型深度总结。

文中就有介绍到在JDK5以及JDK5之后Integer的自动装箱和拆箱。

#Integer缓存策略

虚拟机将创建在一定范围内(-128到127)的整数缓存到一个对象数组,以便进行重用。

缓存:先查缓存,查到了则从缓存中取,没有则重新创建。

为什么不说Int缓存策略而是Integer,说明我们的缓存策略是在自动装箱的时候触发。

那么啥时候会进行自动装箱呢,这样:

Integer a = 10;  //反编译:系统自动执行valueOf方法,拆箱则是:intValue方法。

这样一句代码就完成了自动装箱的操作:调用Integer的“valueOf()”方法。

public static Integer valueOf(int i) {
    //low:-128,high:127。
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);//返回一个Integer对象。
}

IntegerCache是Integer的一个内部类:(cache对象池)

private static class IntegerCache {
    static final int low = -128;
    //jdk6之后可以通过设置jvm的启动参数(-XX:AutoBoxCacheMax=size)调整
    static final int high; 
    static final Integer cache[];

对于指向cache同一数值来讲,通过”=“比较是true的,而像:不同对象地址的比较则是false。(很好理解)

(需要注意:new了一个新的Integer就相当于new了一个新的cache,身处不同池,比较当然为false了。)

Integer i1 = new Integer(123);
Integer i2 = new Integer(0);
i1 = i2;  //false。

什么时候会创建一个Integer对象呢?

if (i >= IntegerCache.low && i <= IntegerCache.high)

在这个范围内,则会去缓存中查找,找不到则会创建对象(两种情况:在这个范围内,但是没有找到,还有一种是不在这个范围内)。

那这样呢?

Integer i1 = new Integer(123);
Integer i2 = new Integer(123);
Integer i3 = new Integer(246);
i3 = i1 + i2; 

结果:true。

思考:前面说了这是一种不同对象池的情况,为什么会是true?

一直在讲装箱,难道你把拆箱给忽略了?对于数学计算,Java是在内存栈中进行操作的,对于i1 + i2,首先会进行拆箱的操作使其变成基本类型(会触发拆箱的运算符:==、+、-、*、/),后面不说你也懂了。

缓存策略有啥好处啊:减少系统创建对象实例的开销——享元模式。

缓存机制不仅应用于Integer还应用于其他数据类型。

#为什么Integer的初始值是null?

明白了Integer的缓存机制,我们就能知道,Integer实际上就是实例对象的引用,为什么这么说,因为Integer的缓存是通过for循环创建存放到一个数组中,该数组会在Integer第一次使用时初始化,结合类加载顺序来理解,第一次使用Integer时内部静态类会被初始化,静态代码块会被执行,存到堆中,并且引用类型的初始值为null。

#开发中尽量避免过多的装箱开箱操作

int值直接存放于内存栈中,虽然数值操作不是原子性的,但是在单线程的存取操作中,开销小,所以速度快很多,不像Integer,还需要牵扯对象头的内存存储以及其他操作,更加的消耗性能。对于对象的内存结构这里不做进一步的介绍。

#为什么我们有时候要使用包装类型数据?

Integer能够方便我们对数据类型之间进行一些转化以及一些进制转化,并且还能解决有些数据结构不支持存放基本数据类型的问题,例如List集合。


对于其他数据类型,这里不做过多的介绍,原理都一样,只不过在范围的约束以及处理上有一定的区别。

这里主要讲解了”=“的比较,下篇文章我们讲从HashMap中来讲解:”==“和”equals“以及hashCode三者之间的关系。

相关推荐
©️2020 CSDN 皮肤主题: 岁月 设计师:pinMode 返回首页