🌲问题来源
??偶然发现<<深入理解jvm>>出了第三版,于是便又拿起来看了,里面有这样的一个代码段,又遇到了自动拆箱,装箱问题,于是一网打尽吧。
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(c == d);
System.out.println(e == f);
System.out.println(c == (a + b));
System.out.println(c.equals(a + b));
System.out.println(g == (a + b));
System.out.println(g.equals(a + b));
----output----
true
false
true
true
true
false
🌲慢慢分析
??什么是自动装箱呢?简而言之,把int转变成Integer 基本类型转变成包装类型,拆箱就是逆操作。
Integer a = 1;
??这段很简单的代码就是一个装箱过程,怎么装箱的呢?通过IDEA调试的时候,选择force step into ,就可以看见来到了这样的代码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
??调用了Integer.valueOf 方法进行了装箱操作,把int转变成为了包装类型。Integer内部提供了[-128, 127]这个区间的缓存,如果说你的这个值是这个区间内的,就从缓存中拿出来给你,这个缓存也就是一个数组Integer cache[] ,如果超过了就是新的一个Integer对象,我还是贴一下这里的代码:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
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);
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
??小知识点:需要注意的是,这个数组Cache只有在第一次使用的时候才会被初始化,原因是,外部类会在jvm启动的时候初始化,执行static代码块,但是静态内部类的代码块并不会执行。可以用下面的代码进行测试:
public class Test {
static {
System.out.println("外部类执行了static代码");
}
private static class Test22 {
static {
System.out.println("内部类执行static代码");
}
}
public static void main(String[] args) {
}
}
----output----
外部类执行了static代码
回到最初的代码:
System.out.println(c == d); :==比较的是地址,c和d都属于缓存内的同一个Integer对象,所以是True 很好理解。System.out.println(e == f); :同理因为超过了那个范围不是一个对象,地址自然不同。System.out.println(c == (a + b)); :首先a+b ,当包装类型遇到加法的时候会先拆箱,因为+ 用于基本类型计算,所以应该是两个int基本类型进行比较的,我们调试看看。public int intValue() {
return value;
}
会执行三次这个方法,返回Integer的基本数据类型,所以最后是3==3 进行比较的。System.out.println(c.equals(a + b)); :a+b 必然是基本类型3,那么看一下c.equals()方法执行过程。调试:
public int intValue() {
return value;
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
值得注意的是如果不是两个Integer类型比较,不管你数值上对不对都是直接返回false的。System.out.println(g.equals(a + b)); 这个就是最好的证明,结果是False。
🌲对于数据计算一劳永逸的方法
不管是浮点数,整数记性计算,都可以用BigDecimal包装一下,既不会出现丢失的问题,也不会出现因为跨类型问题导致的判断错误。demo如下:
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.compareTo(y) == 0) {
System.out.println("true");
}
可能内容和别人差不多,但是自己走一遍会印象深刻,下次也好及时拾起来复习。如有错误,还望多多指教。
后记
最近写的博客少了,不是因为不写,而是觉得没有让我感觉perfect的idea我就不会再写了,不做知识搬运工,不做copy,已有的内容只做整理,让我惊艳的东西再做记录。免得自己都是流水账,哈哈哈哈。下一篇博客估计没多久就来了,因为集合的代码着实让人赞不绝口,还有很多网上搜不到正确答案的点。期待吧。
|