Java是面向对象的语言,但是它含有8种基本数据类型,这些基本数据类型不支持面向对象的编程机制,基本类型的数据也不具备“对象”的特性:没有成员变量,方法可以被调用。
为了解决这8种基本数据类型不能当成Object类型来进行使用的问题,Java提供了包装类的概念,今天所要分析的就是其中的一个包装类 —Integer
首先,我们需要明确一个点:
当包装类和一个值进行比较的时候,虽然包装类型的变量是引用数据类型,但是包装类的实例可以和数值类型的值进行比较,这种比较是直接取出包装类实例所包装的值来进行比较的。
而当两个包装类进行比较的时候,由于包装类的实例实际上都是引用类型,相当于C语言中的指针,所以只有两个包装类引用指向同一个对象的时候,才算是两个包装类的实例相等,最终才会返回true。但是在Java中,自从JDK1.5之后,JDK提供了自动装箱和自动拆箱功能。
自动装箱就是可以直接把一个基本类型赋值给一个包装类实例,而在这个过程中,就会出现一些特别的情形。
Integer a = 1;
Integer b = 1;
System.out.println(a == b);
Integer a = 129;
Integer b = 129;
System.out.println(a == b);
当我们运行上述代码的时候,会发现,上面的代码会输出true,而下面的代码会输出false,那么这是为什么呢?
这和Java的Integer类的设计有关。让我们来查看Java中Integer类的源码,
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
从源码中,我们可以发现,他会进行一个判断,他比较了参数 i 和IntegerCache.low以及IntegerCache.high,如果在IntegerCache.low以及IntegerCache.high之间的话,返回IntegerCache中的值,
如果不在IntegerCache.low以及IntegerCache.high范围内部,则new一个新对象
那么为什么由IntegerCache返回就会指向同一个对象了?
我们继续查看源码
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* jdk.internal.misc.VM class.
*
* WARNING: The cache is archived with CDS and reloaded from the shared
* archive at runtime. The archived cache (Integer[]) and Integer objects
* reside in the closed archive heap regions. Care should be taken when
* changing the implementation and the cache array should not be assigned
* with new Integer object(s) after initialization.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
VM.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {
}
}
从上面的代码中,可以看出,系统会把从-128到127之间的整数自动装箱变成Integer实例,并且放进了一个名字叫做cache的数组中缓存了起来,也就是说,以后把一个再-128到127之间的整数再自动装箱变成一个Integer实例的时候,实际上就是直接指向了对应的数组元素,因此,当比较在-128到127之间的两个Integer实例是否相等的时候,永远都是引用cache数组里面的同一个数组元素,所以才全部相等,而如果不在这个范围内部的话,即如果想要把一个不在-128到127之间的整数自动装箱成Integer实例的时候,系统总是会自动创建一个新的Integer实例,那么即使两个实例的具体值相同,但是由于不是指向同一个对象,所以仍然会输出false。
在上述解释的时候,提到了缓存这一概念,那么什么是缓存,他有什么好处呢?
缓存其实是一种非常优秀的设计模式,举个生活中的例子,如果你想要一台电脑,那么你就去买了一台电脑,但是你不可能一直使用它,总会有电脑不工作的时候,那么在电脑不工作的这段时间,我们如何处理这台电脑呢,一定不会把它扔掉,而是把它放在房间里,等下一次使用的时候,直接开机使用就好了,而不是直接扔掉,等下一次想要使用的时候,再去买一台。这和在内存中的对象有异曲同工之妙,假设电脑就是内存中的对象,而房间就是电脑的内存,那么只要内存足够大,理论上可以把所有使用过的对象都放在房间李,但是房间的大小不是无穷无尽的,我们会把一些只用一两次的东西扔掉,只需要把一些使用的比较频繁内容保存下来就行了,类似的,Java中会把一些创建成本大,需要频繁使用的对象缓存起来,从而提高程序的运行性能。
创建成本大的意思是,想要创建的对象占用系统的内存、系统资源比较多。