zoukankan      html  css  js  c++  java
  • 再学Java 之 Integer 包装类缓存

    前言:本博文将涉及的Java的自动装箱和自动拆箱,可以参考 这篇文章 和 官方教程 ,这里不再赘述。

    首先,先看一个小程序:

    public class Main {
        
        public static void main(String[] args){
            Integer i1 = new Integer(1);
            Integer i2 = new Integer(1);
            System.out.println(i1 == i2);
            
            Integer i3 = 1;
            Integer i4 = 1;
            System.out.println(i3 == i4);
            
            Integer i5 = 200;
            Integer i6 = 200;
            System.out.println(i5 == i6);
        }
    }

    上面的程序会依次输出false 、true、false。

     第一个输出语句应该比较好理解,就是创建了不同的对象。但是第二跟第三个输出语句估计很多人就很难理解了。

    要解释这个问题,需要从缓存说起。

    缓存

      缓存是软件设计模式中一个非常有用的模式,缓存的实现方式有很多,不同方式可能存在性能上的差别。下面给出一个用数组实现的实例:

      (1)缓存类Cache_test

    /*
     * <p>
     * 该对象使用数组实现了缓存,也就是,
     * 每一次使用valueOf()创建新对象时,系统将会确认缓存中是否已经存在相应的对象(即data相等)。
     * 假如存在,则直接返回缓存已存在的对象;
     * 假如不存在,则创建一个新对象,存储到缓存中,并返回新创建的对象。
     * </p>
     * 
     * @author Harvin.
     * @version 1.0
     */
    public class Cache_test {
        //需要存储的数据
        private final String data;
        
        public Cache_test(String data){
            this.data = data;
        }
        public String get_data(){
            return this.data;
        }
        @Override
        //直接判断是否是指向同一个对象
        public boolean equals(Object obj){
            if (this == obj) {
                return true;
            }
            return false;
        }
        
        
        //定义缓存的大小
        private final static int MAX_SIZE = 10;
        //使用数组来存储缓存
        private static Cache_test[] cache
        = new Cache_test[MAX_SIZE];
        //定义当前缓存存储的位置
        private static int pos = 0;
        
        /* 判断是否已经缓存了包含该data对象的Cache_test对象,
         * 如果存在,则直接返回;
         * 如果不存在,则直接创建后再将其返回
         */
        public static Cache_test valueOf(String data){
            for (int i = 0; i < MAX_SIZE; i++) {
                if (cache[i] != null
                        && cache[i].get_data().equals(data)) {
                    return cache[i];
                }
            }
            if(MAX_SIZE == pos){
                cache[0]    = new Cache_test(data);
                pos            = 1;
            }else{
                cache[pos]    = new Cache_test(data);
            }
            return cache[pos++];
        }
    }
    Cache_test

      (2)测试类Main

    public class Main {
        
        public static void main(String[] args){
            Cache_test ct1 = new Cache_test("test1");
            Cache_test ct2 = new Cache_test("test1");
            //由于这里都是直接创建,所以下面会输出false;
            System.out.println(ct1 == ct2);
            
            Cache_test ct3 = Cache_test.valueOf("test2");
            Cache_test ct4 = Cache_test.valueOf("test2");
            //由于这里使用的是valueOf()函数,将会使用到缓存。所以下面输出true.
            System.out.println(ct3 == ct4);
        }
    }
    Main

      上面的例子中,实现原理为:使用一个数组来缓存该类的对象,数组的长度为MAX_SIZE。每一次调用valueOf来创建对象时,缓存池将会先去查找缓存池中是否已经存在该对象,如果存在,则直接返回该对象,所以当输入两个相同data时,返回回来的对象是同一个,所以上面 ct3 和 ct4 为同一个对象。当缓存数组不存在该对象时,缓存池将根据传入的参数创建一个新的对象,再将其存储到缓存数组中。另外,在这里缓存池使用的是“先进先出”的原则。

    PS:上面实例中,用于Cache_test的构造函数为共有,所以,允许创建不存储到缓存池中的对象,假如要强制使用缓存池,则可以将构造函数声明为private。

      

      了解了缓存原理后,我们来看看实际JDK中使用了缓存的类。

    包装类 Integer 的缓存

    类似于我们上面提到的缓存原理,Integer类如果使用new构造函数来创建对象,则每次都将返回全新的对象;假如采用了valueOf方法来创建对象,则会缓存该创建的对象。让我们来看看源码:

    private static class IntegerCache {//内部类,注意它的属性都是定义为static final  
        static final inthigh; //缓存上界  
        static final Integer cache[];//cache缓存是一个存放Integer类型的数组  
      
        static {//静态语句块  
            final int low = -128;//缓存下界,值不可变  
      
            // high value may beconfigured by property  
            int h = 127;// h值,可以通过设置jdk的AutoBoxCacheMax参数调整(参见(3))  
            if (integerCacheHighPropValue !=null) {  
                // Use Long.decode here to avoid invoking methods that  
                // require Integer's autoboxing cache to be initialized  
                // 通过解码integerCacheHighPropValue,而得到一个候选的上界值  
                int i = Long.decode(integerCacheHighPropValue).intValue();  
                // 取较大的作为上界,但又不能大于Integer的边界MAX_VALUE  
                i = Math.max(i, 127);//上界最小为127  
                // Maximum array size is Integer.MAX_VALUE  
                h = Math.min(i, Integer.MAX_VALUE - -low);  
            }  
            high = h; //上界确定,此时high默认一般是127  
            // 创建缓存块,注意缓存数组大小  
            cache =new Integer[(high - low) + 1];  
            int j = low;  
            for(int k = 0; k <cache.length; k++)  
                cache[k] =new Integer(j++);// -128到high值逐一分配到缓存数组  
        }  
      
        private IntegerCache() {}//构造方法,不需要构造什么
    Integer

     简单来说,就是使用了一个内部类IntegerCache 来管理缓存cache[]。但使用valueOf()方法时,系统将会判断是否存在于缓存池中。然而,请注意,这里有所不同的是,Integer类在加载时,就已经预先将一部分对象(即从-128到127)创建好了,也就是说每一次调用valueOf方法时,假如传入的值在-127到128之间,则Integer类直接返回已经创建好的对象,假如传入的参数值在此区间之外,则Integer类会创建一个全新的对象。

    再看小程序

    现在,让我们重新回来一开始的小程序。

    (1)程序中 i1 和 i2 利用其构造函数进行构造,所以,两者是两个不同的对象,因此返回false。

    (2)通过使用javap 查看字节码,可知 i3 和 i4 、i5 和 i6 的自动装箱事实上是调用了valueOf方法。i3 和 i4 的值在-128到127之间,所以直接使用缓存池的对象,而 i5 和 i6 超出该区间,所以创建的是新对象。

    由此便可以得知所以输出结果了。

    后记

    通过资料查找和源码的查看,可以知道,除了Integer类外,还有Byte、Short、Long、Character也使用了缓存,而Flot、Double没有使用缓存。

    相关资料

    Integer中用静态内部类所作的缓存

    Java中的装箱与拆箱

    《Java 自动装箱和拆箱》

  • 相关阅读:
    git使用流程
    php5.3.*编译出现make: *** [ext/gd/libgd/gd_compat.lo] Error 1 解决方法 [转]
    linux用户和组的操作,vi编辑器的使用
    linux目录介绍
    linux和windows分区原理
    linux配置网络,配置lmap
    linux常用操作命令
    mysql数据库编程,内置函数,存储过程(循环插入)
    msql数据库触发器和连接mysql常用函数
    mysql数据库增加删除跟新,关联数据
  • 原文地址:https://www.cnblogs.com/scutwang/p/3705222.html
Copyright © 2011-2022 走看看