Java开发手册-嵩山版:所有包装类对象之间值的比较,全部用equals方法比较。为什么?
例如:
Integer a=100,b=100,c=200,d=200;
System.out.println(a == b); //true
System.out.println(c == d); //false
先回顾一下==和equals的判断方式
- == 比较的是两个变量本身的值,即对象在内存中的首地址。—>深
- equals 比较的字符串中所包含的类容是否相等。—>浅
String s1,s2,s3="abc",s4="abc";
s1 = new String("abc");
s2 = new String("abc");
System.out.println(s1 == s3); //false 两个变量的内存地址不一样
System.out.println(s1 == s2); //false 两个变量的内存地址不一样
System.out.println(s3 == s4); //true
再来看一下自动装箱和拆箱
(1)自动装箱:把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()
、hashCode()
、getClass()
、equals()
等方法。
如Integer a=3
这是自动装箱
其实编译器调用的是static Integer valueOf(int i)
这个方法,valueOf(int i)
返回一个表示指定int值的Integer对象,那么就变成这样:
Integer a=3
相等于 Integer a=Integer.valueOf(3)
(2)拆箱:跟自动装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为基本类型的数据。
如 int i = new Integer(2)
这是拆箱
编译器内部会调用int intValue()
返回该Integer对象的int值
注意: 自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。
为什么需要自动装箱和拆箱:
节省了常用数值的内存开销和创建对象的开销,提高了效率 。
举例:
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
Integer count = 0;
// int count = 0;
for (int i = 0; i < 5000000; i++) {
count += i;
}
System.out.println("计算时长:" + (System.currentTimeMillis() - startTime) + " ms");
}
// 执行结果:
// Integer 计算时长:51 ms
// int 计算时长:6 ms
当数值超过127,自动装箱频繁的new对象、分配内存,造成时间和空间上的性能损耗
**解释:**Integer 类中有个方法public static Integer valueOf(int i)
, 装箱操作
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在-128至127之间的赋值,Integer对象是在IntegerCache.cache
产生的,会复用已有的对象,这个区间的值都可以直接使用 == 进行判断,这个区间外的值就会从新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
* sun.misc.VM class.
*/
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() {}
}
从注释中,我们可以看出,这是个缓存对象,用来提高访问速度。可以使用-XX:AutoBoxCacheMax=<size>
来控制缓存的大小,如果设置 -XX:AutoBoxCacheMax=200
。那上面的两个输出就都是true了。
继续学习开发手册发现这里也有装箱拆箱操作:
三目运算符 condition? 表达式 1 : 表达式 2
中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常。
说明:以下两种场景会触发类型对齐的拆箱操作:
- 表达式 1 或表达式 2 的值只要有一个是原始类型。
- 表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
Integer a = 1,b = 2,c = null;
boolean flag =false;
// a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
Integer result = (flag ? a*b : c);
System.out.println(result);