今天遇见一个有趣的例子:

1
2
3
4
5
6
7
Integer a = 40;
Integer b = 40;
Integer c = 400;
Integer b = 400;

System.out.println(a == b); // true
System.out.println(c == b); // false

这是为啥?原来是Java中存在一个名为IntegerCache的静态类,其中的静态代码块会创建一个包含-128~127的整型数组,若使用Integer创建的值范围在-128~127之间,就直接使用该常量池中的数据,否则就新建一个对象。基于此,重新复习了一遍Java中的基本数据类型包装类与自动装箱、自动拆箱的机制。

一、为什么要有包装类

Java是面向对象语言,其宗旨是一切皆对象。可是,Java中包含着八大基本数据类型,它们不具有对象的特征。于是,便有了包装类,让基本数据类型具有对象的特性,同时拥有一系列自己的属性与操作方法。

二、装箱?拆箱?

看如下定义:

1
2
3
Integer a = new Integer(50); // (1)
Integer b = 50; // (2)
int c = b; // (3)

如上所示,Integer是一个类,理应通过方法(1)使用new关键字创建一个对象,而方法(2)中却可以直接让一个基本数据类型赋值给一个类,方法(3)则将一个对象赋值给了基本数据类型。这之中就使用了装箱(方法2,调用了Integer.valueOf()方法)和拆箱(方法3,调用了Integer.intValue()方法)。其他基本数据类型的拆箱装箱也类似。

三、丰富的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);

System.out.println(i1 == i2); // (1) true
System.out.println(i1 == i2 + i3); // (2) true
System.out.println(i1 == i4); // (3) false
System.out.println(i4 == i5); // (4) false
System.out.println(i4 == i5 + i6); // (5) true
System.out.println(40 == i5 + i6); // (6) true

解释一下上面的6个例子的结果。

  • (1)使用自动装箱调用了Integer.valueOf()方法,其中,传入的int值范围在-128~127之间,则直接从常量池中取对象,故i1i2指向了同一个对象,结果相等。
  • (2)+操作符不适用于类Integer,故i2 + i3的过程中使用了自动拆箱得到结果为40,随后又与对象i1进行比较,i1使用了自动拆箱为40,进行数值比较,结果相等。
  • (3)注意,常量池的使用触发条件是调用了Integer.valueOf()方法,若使用new新建对象,则不会使用常量池中的对象,而是重新建立一个对象,故i1i4指向的是不同的对象,结果不相等。
  • (4)与(3)同理,都是新建了一个对象。
  • (5)与(2)同理,+操作符是基本数据类型操作符,后面的表达式i5 + i6使用了自动拆箱,得到结果为40,再使i4自动拆箱,进行数值比较,结果相等。
  • (6)与(5)同理。

四、有必要提一下==操作符与Object类的equals()方法

  • ==:判断两个对象的地址是否相等,即判断两个对象是否是同一个对象。若==两端是基本数据类型,则比较它们的值是否相等,若是引用类型,则比较它们的内存地址是否相等。
  • equals()方法:同样是判断两个对象的地址是否相等。但该方法可以被重写(覆盖),用于比较值是否相等(如String类中就重写了equals()方法,当两个字符串对象的内容是一样的,就返回true)。

五、那就有必要顺带一提hashCode()方法

为啥重写了equals()方法就必须重写hashCode()方法?

众所周知,hashCode()是用于生成散列码,用于作为散列表的插入与查找标记。当两个对象的hashCode()相同时,表明它们位于桶中的同一个位置,却不代表它们是同一个对象,因为桶中的该位置还会有一个链表用于解决哈希冲突。此时会再调用equals()方法判断是否真的是同一个对象,若不是,则会在链上继续查找(或插入),若是,则返回(或不插入新的)。故两个相同的对象具有相同的hashCode,也令equals()返回值为true;但具有相同hashCode的两个对象不一定是同一对象,也即equals()可能返回false。故重写了euqals()方法也需要重写hashCode()方法。