Java基础面试题中"=="和"equals"的区别是什么?出现的频率还是蛮高的(听别人说的)。知道他们的作用,也就知道他们有什么区别了(这怕不是废话)。但是在使用中出现的结果有时候不是你以为的结果。主要原因是在比较的对象身上。
简单的符号
"=="和"equals"的作用是对两个对象进行比较。具体如下:
- 对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址。
- 对于equals,如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;反之比较的则是所指向的对象的内容。
不简单的对象
基本数据类型
Java中8种基本数据类型:
- 整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte)
- 浮点型:float(4 byte), double(8 byte)
- 字符型: char(2 byte)
- 布尔型: boolean(所占的空间大小并没有给出精确的定义,网上的回答也是五花八门)
注意:equals方法不能作用于基本数据类型的变量。
Java中的基本数据的类型的比较相对而言是最简单的。使用"=="进行比较,判断其是否相等。
String类型
public static void main(String[] args) {
//第一种创建方式
String a = "abc" ;
String b = "abc" ;
//第二种创建方式
String c = new String("abc");
String d = new String("abc");
System.out.println(a==b);//true
System.out.println(a.equals(b));//true
System.out.println(c==d);//false
System.out.println(c.equals(d));//true
System.out.println(a==c);//false
System.out.println(a.equals(c));//true
}
上面的例题非常简单,对字符串"abc"进行比较。
- 对于"equals",因为String中对equals进行了重写,比较的是对象的内容,所以比较结果都是true,对于这个结果在意料之中。
- 对于
“==”
,结果如果让你皱起了眉头,那就继续往下看,再次提醒“==”
作用引用类型变量时,比较的是所指向的对象的地址。
使用第一种方式时,首先在常量池中查询并通过equals来判断是否已经存在"abc",如果有就不会创建的新的对象,直接返回已有对象的引用,由于String类被final修饰,所以它一经创建就不会被修改。也就不必担心共享而带来的问题。也就不难理解为什么a==b的结果为true。
使用第二种方式时,无论常量池中有没有"abc"字符串,都会在堆内存中开辟一片新的空间存放对象,即使堆内存中存在"abc",也会开辟一块新的空间存放对象,所以c==d
、a==c
的结果则均为false。
Integer类型
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);//true
System.out.println(i1.equals(i2));//true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);//false
System.out.println(i3.equals(i4));//true
}
上面已经了解了String类型,接下来我们来看下Integer类型。Integer是Java为基本类型int提供的包装类。
Java 为基本数据类型提供了对应的包装器类型
- Integer:对应封装了基本类型 int;
- Long:对应封装了基本类型 long;
- Float:对应封装了基本类型 float;
- Double:对应封装了基本类型 double;
- Boolean:对应封装了基本类型 boolean;
- String:对应封装了字符串类型 char[]。
包装类型与基本类型之间的相互转换称作装箱和开箱。
所谓的自动装箱,就是自动将基本数据类型转换为包装器类型。所谓的自动拆箱,也就是自动将包装器类型转化为基本数据类型。
数值是基本数据类型 int,当赋值给包装器类型(Integer)变量的时候,触发自动装箱操作,创建一个 Integer 类型的对象,并且赋值给变量。
- 对于"equals",因为比较的是对象的内容,所以比较结果都是true,对于这个结果在意料之中。
- 对于
"=="
,上面提到触发自动装箱时,会创建一个对象,按这说法来看,i1 == i2
、i3 == i4
的结果应该都是false,为什么i1 == i2的结果是true呢?
实际上,当通过自动装箱,也就是调用 valueOf() 来创建 Integer 对象的时候,如果要创建的 Integer 对象的值在 -128 到 127 之间,会从 IntegerCache 类中直接返回,否则才调用 new 方法创建。看代码更加清晰一些,Integer 类的 valueOf() 函数的具体代码如下所示:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
复制代码
为什么 IntegerCache 只缓存 -128 到 127 之间的整型值呢?
在 IntegerCache 的代码实现中,当这个类被加载的时候,缓存的享元对象会被集中一次性创建好。毕竟整型值太多了,我们不可能在 IntegerCache 类中预先创建好所有的整型值,这样既占用太多内存,也使得加载 IntegerCache 类的时间过长。所以,我们只能选择缓存对于大部分应用来说最常用的整型值,也就是一个字节的大小(-128 到 127 之间的数据)。
实际上,JDK 也提供了方法来让我们可以自定义缓存的最大值,有下面两种方式。如果你通过分析应用的 JVM 内存占用情况,发现 -128 到 255 之间的数据占用的内存比较多,你就可以用如下方式,将缓存的最大值从 127 调整到 255。不过,这里注意一下,JDK 并没有提供设置最小值的方法。
//方法一:
-Djava.lang.Integer.IntegerCache.high=255
//方法二:
-XX:AutoBoxCacheMax=255
复制代码
实际上,除了 Integer 类型之外,其他包装器类型,比如 Long、Short、Byte 等,也都利用了享元模式来缓存 -128 到 127 之间的数据。