关于int和Integer缓存(一):以及设计构想(享元模式)

关于Integer的值缓存:
在介绍Integer的值缓存之前,我们需要了解的是,java中的包装类型,
我们都知道java中有包装类型
int                     Integer   
double             Double      
char                 Character
boolean           Boolean     

... 

为什么要使用包装?

包装类型源于java万物皆对象的思想
1.因为基本类型不具备对象的特性,所以就出现了包装类型(想象如果没有包装类型,Collection等集合中,就不能存放基本类型,泛型的使用就出现了尴尬点咯)
2.包装类提供的丰富的方法也简化和帮助我们完成了一些繁琐的操作(类型转化一系列parse操作)
3.假设一个业务需求,其中涉及到对多个值进行判断,最常见检测初始化,如果是int 类型等等基础类型,例如:
byte         0            
short0            
int         0            
long         0L          
float         0.0f        
double0.0d       
char         'u0000'
booleanfalse      

这个时候,包装类型就难得可贵了,包装类型的默认值都是null!



但是为什么java不直接使用包装类型,同样保留了基础类型?

因为一些小的变量,通过对象的形式存储在堆中,就很浪费空间,而且操作也
没有那么高效了!

所以java中有了包装和基本类型


那么自动装箱和拆箱是怎么发生的呢?

因为包装类本质是引用类型,对象本身不能像基础类型一样进行操作,

例如:int、double、float的加减乘除(这里顺便一提,double有设计缺陷,进行减价乘除操作的时候,会出现精度错误和值错误的问题,需要使用:BigDecimal,请自行了解!)

 

所以在进行加减操作,把Integer的值赋给int类型的变量的时候,就会出现自动拆箱;

Integer n1 = new Integer(18);
n1 += 10;  
n1++; 
n1--;
int n2 = n1;
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); // 这里也会自动拆箱



在直接给Integer类型赋值时候(不通过new的方式进行赋值),就会出现自动装箱:

Integer n1 = 999;

首先我们做一个看似无关的测试,来巩固我们的思考:
如下运行:

Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false


Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true


Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false





通过上面的例子,我们一一分析:
1.都是引用类型,对象比较堆空间中地址不同,肯定不相等!
2.第二个 一个是int类型,一个是Integer类型,因为Integer对应的是int的包装类,所以在进行比较之前要进行一次拆箱。
3.两个不同的Integer的声明方式一个是通过new对象的方式进行的,一个是通过直接赋值进行的,因为前一个肯定是引用类型,只要一边出现了引用类型,那肯定是不相等的!堆中地址不同。



Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true

Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false

注意上面,为什么我这里写的是128就不行,100就可以呢


解释,分析以下源码片段(Integer):

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


        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;


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


            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }


        private IntegerCache() {}
    }

       其中在第一个方法中有一个high和low,并且明确讲了,只有在low和hgih之间的数字才会进入内部类IntegerCache,
在IntergerCache中就做了一件事情:就是如果传入的数字是[-128, 127]之间数字,就直接存储进常量池中;如果不在这个范围内,就new Integer(xx)进行返回。
所以其实我们的一句 Integer n1 = 128 其实是通过了Integer内部进行new操作自动装箱的!

那为什么Integer要进行对-127到128之间的数据进行存入常量池中进行操作呢?
        这里就涉及到java的23中设计模式中的享元模式了:这里简单介绍一下享元模式[享元模式就是把一些常用的常量等
共享出来,重复高效的去使用,以达到节约内存和提高效率的目的,JDK5.0之后的enum也是享元模式的一种体现
]






学习参考,感谢:
https://www.zhihu.com/question/22775729
http://simon-c.iteye.com/blog/1016031
https://baike.baidu.com/item/%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F/10541959
http://baijiahao.baidu.com/s?id=1584080745080984291&wfr=spider&for=pc
https://www.jianshu.com/p/9bd18eae9a1a

https://blog.csdn.net/chenliguan/article/details/53888018

原文地址:https://www.cnblogs.com/mzywucai/p/11053408.html