Integer源码解读
该源码基于 jdk 1.8.0_261 版本。
一:问题导入
Integer a=500;
Integer b=500;
System.out.println(a==b);
System.out.println(a.equals(b));
Integer c=90;
Integer d=90;
System.out.println(c==d);
System.out.println(c.equals(d));
Integer e = 127;
Integer f = new Integer(127);
System.out.println(e==f);
Integer g = 128;
int h = 128;
System.out.println(g==h);
发现结果是:
看到这里我就有点迷了,equals是用来比较两个对象的值是否想等,这个都为true没问题,但是 “==” 不就是用来比较两个对象的地址是否相等吗,按理说应该都为false呀,那为什么一个为false,一个为true呢?
于是就想深入的去看一下源码。
从中我们可以看出 500 在初始化的时候是new 了一个新的 Integer 对象;
而 90 在初始化时:是在 IntegerCache 中取的值。
那我们接着往下看 IntegerCache :
这是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) {
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() {}
}
默认缓存的范围是 [-128,127],当Integer的值范围在 [-128,127] 时则直接从缓存中获取对应的Integer对象,不必重新实例化。这些缓存值都是静态且 final 的,避免重复的实例化和回收。
我们再看看控制台:
发现c 和 d 的地址确实是相同的,所以问题解决!!
那么 e 和 f 是怎么回事呢?
还是通过源码:Integer e,e 是从 IntegerCache 中取到的,但是 f 是 又 new 出来的,它们两个肯定不是一个对象,所以肯定为 false 。其实不管 e 是不是从 IntegerCache 中取出来的,它和 new 出来的 f 也肯定不是一个对象。肯定为 false 。
那么 g 和 h 是怎么回事呢?
凡是涉及到基本数据类型, == 号比对的都是值,因为包装类都会自动拆箱。
二:equals方法
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
public int intValue() {
return value;
}
这个没什么说的,显而易见,也比较常用。
三:自动装箱拆箱
装箱就是自动将基本数据类型转换为包装器类型;
拆箱就是自动将包装器类型转换为基本数据类型。
下表是基本数据类型对应的包装器类型:
//自动装箱
Integer a = 99;
//自动拆箱
int b = a;
这个过程是自动执行的:
Integer a = 99;
执行上面那句代码的时候,系统为我们执行了:
Integer a = Integer.valueOf(99);
int b = a;
执行上面那句代码的时候,系统为我们执行了:
int b = a.intValue();
1、谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。
1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;
2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般情况下要优于第一种情况。
因为如果直接new Integer的话,它就会调用构造方法new出来一个Integer对象出来。但是要是用Integer i = xxx,它会调用valueOf方法,如果数的范围在IntegerCache范围内时,那么它就不需要再new出来一个对象,直接就从这个缓冲池中直接取出数据了
2、Double类的valueOf方法
出于好奇,就想看看其他的包装类是什么样的情况:
猜猜,下面的结果:
Double a = 50.0;
Double b = 50.0;
System.out.println(a==b);
Double c = 200.0;
Double d = 200.0;
System.out.println(c==d);
答案是:
看看valueOf源码:
public static Double valueOf(double d) {
return new Double(d);
}
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s));
}
发现它是没有缓冲的,都是直接new对象。
为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现?
很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。
3、Boolean类的valueOf方法
Boolean a = false;
Boolean b = false;
System.out.println(a==b);
Boolean c = true;
Boolean d = true;
System.out.println(c==d);
看看valueOf源码:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
TRUE 、 FALSE两个常量
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
注意:
Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double、Float的valueOf方法的实现是类似的。
四:问题升级
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
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));
System.out.println(g.equals(a+h));
当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。
第一个和第二个输出结果没有什么疑问。第三句由于 a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。
而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样。
不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。
未完待续!!!