包装类型、拆线装箱和常量池缓存

包装类型

数据类型

Java中存在8种基本类型和5种引用类型。

基本数据类型

基本数据类型分为3类:字符,布尔,数值类型

  • char:16位
  • byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。
  • short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。
  • int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。
  • long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。
  • float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。
  • double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。
  • boolean:只有true和false两个取值。

对于较小、基础的的变量,作为对象储存不如直接作为值储存在堆栈中

引用数据类型

五种引用类型分别为:

  • 接口
  • 数组
  • 枚举
  • 标注

包装类型的出现

每个基本类型都有对应的包装类型

Java是一个面向对象的语言,基本类型并不具有对象的性质,为了与其他对象“接轨”就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。

区别

基本类型的优势:数据存储相对简单,运算效率比较高 包装类的优势:提供对数据更多操作,比如集合的元素必须是对象类型,满足了java一切皆是对象的思想 声明方式不同,基本类型不适用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间; 存储方式及位置不同,基本类型是直接将变量值存储在堆栈中,而包装类型是将对象放在堆中,然后通过引用来使用; 初始值不同,基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null 使用方式不同,基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到

装箱拆箱

装箱即基本类型转为对应包装类型的过程,相应地拆箱即逆过程。

自动拆箱装箱

当对基本类型使用包装类操作或者对包装类进行基本类型操作(如:操作符)时,自动地转换为相应的包装类或基本类型。以达到屏蔽转换的过程,对外只表现为对变量的操作。

JDK源码

使用了了包装类中的newValueOf和xxxValue()方法,也可以由我们自己手动调用,相当于一种语法糖.

缓存机制

以Integer类为例,装箱机制创建包装类对象时,首先会判断数值是否在-128—-127的范围内,如果满足条件,则会从缓存(常量池)中寻找指定数值,若找到缓存,则不会新建对象,只是指向指定数值对应的包装类对象,否则,新建对象。

对应JDK源码为:IntegerCache

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];//声明为final,所以缓存的对象会被放入常量池中;声明为statci,所以是在类加载的时候就创建好了
        //创建-128~127的值的包装类对象
        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() {}
}
​
复制代码

当我们定义两个Integer的范围在【-128—+127】之间,并且值相同的时候,用==比较值为true;

当大于127或者小于-128的时候即使两个数值相同,也会new一个integer,那么比较的是两个对象,用==比较的时候返回false

享元模式,服用常用的变量对象

Integer 、Byte 、Short 、Long 、Character 五大包装类都有缓冲机制,且缓冲的默认值范围都是-128~127

而Float,Double,Boolean 三大包装类并没有缓冲机制。

缓存机制的设计

IntegerCache是Integer的内部类,用来将-128——high之间的对象进行实例化

这边固定了缓存的下限,但是上限可以通过设置jdk的AutoBoxCacheMax参数调整,自动缓存区间设置为[-128,N];

IntegerCache 不会有实例化,它是 private static class IntegerCache,在 Integer 中都是直接使用其 static 方法