[1] 先讨论一个面试题
int a = 1; Integer b = 1; Integer c = new Integer(1); Integer d = Integer.valueOf(1); int e = d; int f = d.intValue();
请问以下式子的值?为什么?
a == b // true a == c // true b == c // false
[2] "=="与"equals"用法的不同
对于Java
中的判断相等,有两种方法:
· 操作符==
· 方法equals
对于==
操作符来说,区分是基本类型还是引用类型:
1、如果比较的数据是基本类型,则比较它们的值;
2、如果比较的是对象,则会比较对象的内存地址。
3、另外,如果一个是基本类型、一个是包装类型,在比较前会先把包装类型拆箱成基本类型,然后进行比较。
equals
方法是用来判断其他的对象是否和该对象相等,equals()
方法在Object
类中定义如下:
public boolean equals(Object obj) { return (this == obj); }
很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们知道,String 、Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。
所以对于equals来说,区分调用者是否重写了equals方法:
1、类没有重写equals()方法:通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是Object类equals()方法,比较它们的地址值。
2、类重写了equals()方法:一般我们都重写equals()方法来比较两个对象中的属性是
否相等;若它们的属性相等,则返回true
(即认为这两个对象相等)。
【注意】
equals()
不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。
[3] 基本类型和包装类型的区别
1、对于基本类与包装类的转换,Java
提供了装箱、拆箱机制;
2、包装类的缓存机制。Byte
, Short
, Integer
, Long
这 4 种包装类默认创建了数值 [-128,127]
的相应类型的缓存数据, Character
创建了数值在[0,127]
范围的缓存数据,Boolean
直接返回 True
or False
;
3、Java语言是面向对象的,但是Java
的基本数据类型不是,将基本数据类型设计成类,可以增强Java
面向对象的性质;
4、包装类可以用于泛型,而基本类型不可以;
5、基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。包装类型需要占用更多的内存空间。假如没有基本类型的话,对于数值这类经常使用到的数据来说,每次都要通过new
一个包装类型就显得非常笨重。
[4] 3种创建基本类型的方式
以int
为例,这里我们把参与比较的类型分为三种:int、直接new出来的Integer对象和自动装箱出来的Integer对象。分类细节如图:
[4.1] int
int a = 1; Integer b = 1; int e = b; int f = b.intValue();
如上代码中,变量a、e、f
的创建:
a
属于直接创建;
e
是对Integer
类型进行自动拆箱,转化成int
类型;
f
是调用变量b
的intValue()
方法,可以理解为显式的拆箱。
[4.2] 直接new出来的Integer对象
Integer c = new Integer(1);
没啥好说的。包装类型存储的是堆中的引用。
[4.3] 自动装箱出来的Integer对象
Integer b = 1; Integer c = 128; Integer d = Integer.valueOf(1);
如上代码中,变量b、c
的创建使用了自动装箱机制,自动装箱机制会调用指定包装类的valueOf
方法,像变量d
那样。
由于具有包装类自动装箱时具有缓存机制,而它就是通过valueOf
方法实现的,其代码如下:
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
自动装箱定义时会返回缓存中数据。
[5] 综上,我们回到面试题
int a = 1; // 直接定义,存在栈中 Integer b = 1; // 自动装箱,且1在[-128,127],b是缓存中数据 Integer c = new Integer(1); // new对象,存在堆中的引用
由于对于操作符==
,如果一个是基本类型、一个是包装类型,在比较前会先把包装类型拆箱成基本类型,然后进行比较。所以a == b
、a == c
都为true。
对于同样是包装类型的变量b、c
,操作符==
比较的是它们的引用,而一个是缓存中数据,一个在堆中,所以b == c
为false。
a == b // true a == c // true b == c // false
【注意】对于变量b、c的比较,它们是引用类型了,我们应该使用equals()来判断它们的值是否相等,IDEA会有提示。而变量a、b,变量a、c之间的比较,不能用equals(),因为基本类型不可以使用equals()。