• 关于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

  • 相关阅读:
    dynamic 转换实体类
    泛型的简单使用
    winfrom嵌入word
    echart睡眠后台代码,
    echart实现睡眠前台代码
    Mysql时间加一天
    一道关于面向对象面试题所引发的血案(阿里)
    一道面试题让你彻底掌握JS中的EventLoop(头条)
    对象(数组)的深克隆和浅克隆(头条)
    BAT笔试题中几道关于堆栈内存和闭包作用域的题
  • 原文地址:https://www.cnblogs.com/mzywucai/p/11053408.html
Copyright © 2020-2023  润新知