自动拆箱和装箱(详解)
静态方法 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后,常量池被放入到堆。