[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对象。分类细节如图:

Java基本类型和包装类什么情况下判断相等(“==“或“equals“)?_java

[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()​​。