夯实Java基础系列2:Java自动拆装箱里隐藏的秘密

自动拆箱和装箱(详解)

静态方法 valueOf(基本类型): 基本类型 --> 包装类型;如Integer.valueOf(10);
实例的方法 xxxValue(): 包装类型–> 基本类型;如 Integer包装变量.intValue();

自动装箱与拆箱中的“坑”

在使用自动装箱与自动拆箱时,要注意一些陷阱,为了避免这些陷阱,我们有必要去看一下各种包装类型的源码。
Integer源码

public final class Integer extends Number implements Comparable<Integer> {
	private final int value;
	

/*这是Integer的构造方法,接受一个整型参数,Integer对象表示的int值,保存在value中*/
 public Integer(int value) {
        this.value = value;
 }
 
/*equals()方法判断的是:所代表的int型的值是否相等*/
 public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
}
 
/*返回这个Integer对象代表的int值,也就是保存在value中的值*/
 public int intValue() {
        return value;
 }
 
 /**
  * 首先会判断i是否在[IntegerCache.low,Integer.high]即[-128,127]之间
  * 如果是,直接返回Integer.cache中相应的元素
  * 否则,调用构造方法,创建一个新的Integer对象
  */
 public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
 }

/**
  * 静态内部类,缓存了从[low,high]对应的Integer对象
  * low -128这个值不会被改变
  * high 默认是127,可以改变,最大不超过:Integer.MAX_VALUE - (-low) -1
  * 									 即2的31次方-(128) -1
  * cache 保存从[low,high]对象的Integer对象
 */
 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) {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127); //这两行是为了一定确保h=127
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1); //这两行是为了一定确保h=127
        }
        high = h;
 
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }
 
    private IntegerCache() {}
}

通过分析上面的代码,得到:

1)Integer有一个实例域value,它保存了这个Integer所代表的int型的值,且它是final的,也就是说这个Integer对象一经构造完成,它所代表的值就不能再被改变。

2)Integer重写了equals()方法,它通过比较两个Integer对象的value,来判断是否相等。

3)重点是静态内部类IntegerCache,通过类名就可以发现:它是用来缓存数据的。它有一个数组,里面保存的是连续的Integer对象。
(a) low:代表缓存数据中最小的值,固定是-128。

(b) high:代表缓存数据中最大的值,它可以被该改变,默认是127。high最小是127,最大是Integer.MAX_VALUE-(-low)-1,如果high超过了这个值,那么cache[ ]的长度就超过Integer.MAX_VALUE了,也就溢出了。

© cache[]:里面保存着从[low,high]所对应的Integer对象,长度是high-low+1(因为有元素0,所以要加1)。

4)调用valueOf(inti)方法时,首先判断i是否在[low,high]之间,如果是,则复用Integer.cache[i-low]。比如,如果Integer.valueOf(3),直接返回Integer.cache[131];如果i不在这个范围,则调用构造方法,构造出一个新的Integer对象。

5)调用intValue(),直接返回value的值。
通过3)和4)可以发现,默认情况下,在使用自动装箱时,VM会复用[-128,127]之间的Integer对象。

在这里插入图片描述
上图表明
Integer默认缓存是-128到127之间的对象,上限127可以通过修改JVM参数来更改。也就是配置 sun.misc.VM.getSavedProperty(“java.lang.Integer.IntegerCache.high”);相关参数
深入浅出JAVA包装类及面试题陷阱

此外
int的自动拆箱和装箱只在-128到127范围中进行,超过该范围的两个integer的 == 判断是会返回false的。

对于基本数据类型,== (双等号)比较的是值,而对于包装类型,==(双等号)比较的则是2个对象的内存地址。

经查看源码发现
1.Integer类型有缓存[-128,127]的对象。缓存上限可以通过配置jvm更改
2.Byte,Short ,Long 的cache长度都是256 缓存[-128,127]
3.而Character的cache长度为 128 缓存[0,127]
4. 而Float ,Double则没有cache (大概是因为浮点数本来就不精确 不好比较cache中的数是否相等)

尤其需要特别注意的是,只有valueOf方法构造对象时会用到缓存,new方法等不会使用缓存!

遇到的坑

1.比较的数都在 [-127,128] 范围 内 但是为什么==返回false 而equal返回true?
在这里插入图片描述在这里插入图片描述
1.是因为 用 == 的时候 对于基本类型(int)来说是值比较,对于引用类型来说是比较的是引用 也即地址; 那么一个是缓存里的 一个是new出来的自然不一样了
2.而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法, 比如 String、Integer 等把它变成了值比较,所以这个情况下 equals 比较的是值是否相等。
你真的懂 == 和 equals 的区别吗?
plus:那对于超过缓存范围的话两个Integer的数比较 而不是new的比较 == 是false 而equals还是true(因为重写了equals方法)
在这里插入图片描述
在这里插入图片描述
此外不管是
1.超过缓存
2.或是new出来的包装类
在比较时都是按照基本类(int)来 直接比较数值的
在这里插入图片描述
在这里插入图片描述
因为 包装类型 == 基本类型时,包装类型自动拆箱;
需要注意的是: == 在没遇到算术运算时,不会自动拆箱;基本类型只会自动装箱为对应的包装类型,代码中最后一条说明的内容。‘
也即上面的 d 自动包装为Integer 那么根据之前说的 == 会首先判断是否是同个引用 所以返回true
一道面试题关于Integer的缓存范围(-128~127)所引起的一系列问题记录

基本数据类型的存储方式

方法中变量存在栈中的局部变量表
类实例化的对象存在堆中
基本数据类型的包装类型可以在常量池查找对应值的对象,找不到就会自动在常量池创建该值的对象
JDK1.7后,常量池被放入到堆

参考资料

发布了81 篇原创文章 · 获赞 19 · 访问量 3614

猜你喜欢

转载自blog.csdn.net/c22cxz/article/details/104719330
今日推荐