文章目录
判断两个对象是否相等时,有两种方法,==
和 equals()
。
-
==
: 它的作用是判断两个对象的地址是不是相等。对于基本类型,比较的是值,对于对象,比较的是对象的存放地址。 -
equals()
: 它的作用也是判断两个对象是否相等。- 如果类中覆盖了该方法,那么通常是比较两个对象的内容是否相等。如果相等,则返回
true
;如果不等,则返回false
。 - 如果类中没有覆盖该方法,那么比较的是两个对象的地址是否相等,等价于
==
。
- 如果类中覆盖了该方法,那么通常是比较两个对象的内容是否相等。如果相等,则返回
在举例子之前,先说一下基本类型和包装类型的东西。
1. 基本类型和包装类型
1.1 包装类型
基本类型有8种,都有各自的默认值,默认值均不为null
,分别是:
-
6 种数字类型 :
byte
、short
、int
、long
、float
、double
-
1 种字符类型:
char
-
1 种布尔型:
boolean
1.2 包装类型
包装类型是基本类型所对应的对象。由于包装类型是对象,所以可以用于泛型,
1.3 信息总结
这 8 种基本数据类型的默认值以及所占空间的大小如下:
基本类型 | 位数 | 字节 | 默认值 | 对应的包装类型 | 包装类的默认值 | |
---|---|---|---|---|---|---|
数字型 | short | 16 | 2 | 0 | Short | null |
int | 32 | 4 | 0 | Integer | null | |
long | 64 | 8 | 0L | Long | null | |
字符型 | byte | 8 | 1 | 0 | Byte | null |
char | 16 | 2 | ‘u0000’ | Character | null | |
数字型 | float | 32 | 4 | 0f | Float | null |
double | 64 | 8 | 0d | Double | null | |
布尔型 | boolean | 1 | — | FALSE | Boolean | null |
1.4 在哪里放着
-
基本类型的局部变量在栈中的局部变量表中存放,基本类型的成员变量在堆中存放。
-
包装类型是对象类型,几乎所有的对象类型都在堆中。
小纸条
如果类中成员变量使用基本类型(如 int
),并且不被static
修饰的话,会存放在堆中。
这种情况,使用基本类型对应的包装类型更好。
class Student {
private int age;
}
// 改为包装类型
class Student {
private Integer age;
}
1.5 包装类型的缓存机制
包装类型会用缓存机制来提高性能。各个包装类型的缓存能数据范围如下:
包装类型 | 缓存数据范围 | |
---|---|---|
数字型 | Short | [ -128, 127 ] |
Integer | [ -128, 127 ] | |
Long | [ -128, 127 ] | |
字符型 | Byte | [ -128, 127 ] |
Character | [ 0, 127 ] | |
布尔型 | Boolean | true / false |
如果要创建的包装类型的数据在缓存中能找到,那么会直接使用缓存中的数据,而不会创建新的对象。比如
Integer i1 = 10; // 10 < 127,i1使用缓存中的数据
// 判断 i1 的情况
// 如果 i1 使用缓存中的数据,没有创建新对象,那么再创建一个值为10的对象,
// 两个对象的地址应该是相同的,都是缓存中的数据的地址
Integer i12 = 10;
System.out.println(i1 == i12); // 输出结果为 true
Integer i2 = 128; // 128 > 127,i2创建新的对象
// 判断 i2 的情况
// 如果 i2,创建了新对象,没有使用缓存中的数据,那么再创建一个值为128的对象,
// 两个对象的地址应该是不同的,因为两个对象的地址不会相同
Integer i22 = 128;
System.out.println(i2 == i22); // 输出结果为 false
1.6 自动拆箱与装箱
自动装箱和拆箱,是自动在基本类型和包装类型之间进行转换,这种转换是在赋值、方法调用时等情况下发生的,比如
// 基本类型 转为 包装类型
Integer a = 200;
// 200是基本类型,但是可以直接赋给 Integer 变量a ,而不用进行类型转换
// 如果没有自动装箱,就只能使用 Integer a = new Integer(200); 来创建变量
// 基本类型 转为 包装类型
int b = a; // a 是包装类型,但是可以直接赋给int类型 ,而不用进行类型转换
// a 是 Integer类型,b 是 int 类型,如果没有自动拆箱,就需要自己进行类型转换
自动拆箱和装箱,是在本来需要自己进行类型转换的时候,交给Java进行,而不用自己强制转换。
2. 比较
-
基本类型和基本类型比较,使用
==
即可。 -
基本类型和包装类型比较,使用
==
。 -
对象和对象比较,使用
equal()
。
2.1 数字比较
基本类型和基本类型比较,比较的是值是否相等。
int a = 10;
int b = 10; // b == a
int c = new Integer(10); // c == a
基本类型和包装类型比较,比较的也是值是否相等。
int a = 10;
Integer aa = new Integer(10); // aa == a
包装类型和包装类型比较,比较的是地址是否相等。
Integer a = new Integer(10);
Integer b = new Integer(10); // b != a,不是同一个对象,地址不同
2.2 String 比较
String
是一个常量,存放在运行时常量池(在方法区/元空间)。是不可变的(在创建之后无法更改),它的底层是由char[]
实现的。比如:
String str = "abc";
// 等价于
char[] data = {'a', 'b', 'c'};
String str = new String(data);
由于String
对象是不可变的,所以它们是可以被共享的。在创建新的String
对象时,如果常量池中存在该对象的值,那么不会创建新的字符串,而是会直接使用常量池中存在的字符串。如果常量池中不存在该对象的值,那么会在常量池中创建字符串。
所以,比较两个类型为 String
的字符串是否相等,最好使用equals()
。
// 使用常量池的数据
String s1 = "abc"; // 在常量池中创建一个 abc 字符串
String s2 = "abc"; // 使用常量池中的数据
System.out.println(s1 == "abc"); // true。s1 和 "abc" 的地址相同
System.out.println(s2 == "abc"); // true。s2 和 "abc" 的地址相同
System.out.println(s1 == s2); // true。s1 和 s2 指向的是同一个对象
System.out.println(s1.equals(s2)); // true.s1 和 s3 的值相等
// 使用 new 创建新对象
String s3 = new String("abc"); // 创建一个新对象
System.out.println(s3 == "abc"); // false。s3 和 "abc" 的地址相同,s3 使用的不是常量池中的数据
System.out.println(s3 == s1); // false。s1 和 s3 不是一个对象
System.out.println(s1.equals(s3)); // true。s1 和 s3 的值相等
String s4 = "def"; // 在常量池中创建一个 def 字符串
System.out.println(s1 == s4);
字符串和对象引用的示意图如下:
2.3 对象比较
比较两个对象的地址是否相同,如果相同则返回 true
,如果不同则返回 false
。
// 创建一个 Integer 对象
Integer a = new Integer(10);
// 使用两个变量作为对比
Integer b = new Integer(10); // b != a,不是同一个对象
Integer c = a; // c == a,是同一个对象
对象和对象引用的示意图如下:
3. HashMap相关
由于 HashMap
要说的内容和上面的比较情况稍有不同,这里把 HashMap
中想要说的东西单独拎出来。
1)containsKey()
HashMap
中常常需要判断某个值是否在HashMap
中,会用到containsKey()
。该方法判断某个值在HashMap
中是使用equals()
来判断的, 也就是说,如果一个类覆盖了这个方法,那么是并根据值来判断的;如果一个类没有覆盖这个方法,那么是根据地址来判断的。
containsKey()
有说到,key==null ? k==null : key.equals(k)
(其中,key
是HasMap
中的键,k
是要差值的值)当哈希表中键不为空并且等于其中一个键值时,返回true
。简短的看,containsKey()
就是使用 equal()
方法比较key
和k
是否相等。
举个String
例子:
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
String s = new String("a"); // 创建一个对象
// 比较的是s和map中的键值是否有相等的。因为String重写了 equals() 方法
System.out.println(map.containsKey(s)); // true
举个一维数组的例子:
Map<int[], Integer> map = new HashMap<>();
map.put(new int[]{1, 2}, 1);
map.put(new int[]{2, 3}, 2);
map.put(new int[]{3, 4}, 2);
int[] array = new int[]{1, 2}; // 创建了一个新数组
// 比较的是array和map中的键值的地址是否有相等的。因为 int[]没有重写 equals()方法
System.out.println(map.containsKey(array)); // false
小纸条
如果需要比较两个数组是否相等时,需要使用 Arrays
类的 equals()
方法,比如
int[] array1 = {1, 2};
int[] array2 = {1, 2};
Arrays.equals(array1, array2); // true
2)value比较时的一个情况
值value 和 基本类型比较
先说一种比较常见的情况,我们在创建一个 HashMap
对象为 map1
后,有时会需要比较 HashMap
中是否包含某个值,一般使用 containsValue()
判断是否数据包含在 HashMap
中(containsValue()
的使用和 containsKey()
类似)。比如
Map<Character, Integer> map1 = new HashMap<>();
map1.put('a', 1);
map1.put('b', 200);
// 一般使用 containsValue() 判断是否数据包含在 HashMap 中。比如
System.out.println(map1.containsValue(1)); // true
System.out.println(map1.containsValue(200)); // true
为了说明下一步的例子,这里使用 ==
进行比较,给出比较的结果比如
Map<Character, Integer> map1 = new HashMap<>();
map1.put('a', 1);
map1.put('b', 200);
// 但是为了说明下面的例子,这里使用 == 进行比较,比如
System.out.println(map1.get('a') == 1); // true
System.out.println(map1.get('b') == 200); // true
这里,map.get()
得到的数据类型是Integer
,在和基本类型 int
比较时,相当于两个基本类型int
比较,比的是值是否相等。
值value 和 包装类型比较
下面说的一种情况,我不知道什么时候用到是合适的,一个朋友遇到时,也是挺想不到的。
创建一个 HashMap
对象为 map1
,存储一些数据。
创建另一个 HashMap
对象为 map2
,存储和map1
相同的数据。
Map<Character, Integer> map1 = new HashMap<>();
map1.put('a', 1);
map1.put('b', 200);
// 创建另一个 HashMap ,存储相同的数据
Map<Character, Integer> map2 = new HashMap<>();
map2.put('a', 1);
map2.put('b', 200);
// 比较
System.out.println(map2.get('a') == map1.get('a')); // true
System.out.println(map2.get('b') == map1.get('b')); // false
// 如果要比较,需要使用 equals()
System.out.println(map2.get('a').equals(map1.get('a'))); // true
System.out.println(map2.get('b').equals(map1.get('b'))); // true
这里出现这样的结果,是一个对象和一个对象的比较(本章2.1),比较的是map
中存储对象的地址是否相等。