==
与 equals()
==
和 equals()
的行为常常令人迷惑,尤其是在处理字符串、集合、包装类以及自定义对象时。
本文将深入剖析两者的本质区别、使用场景和最佳实践,并带你避开那些常见的坑。
==
和 equals()
?看下面的例子:
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
两个内容一样的字符串,用 ==
得到的是 false
,但用 equals()
却是 true
。为什么?
再比如:
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true(意外?)
Integer x = 200;
Integer y = 200;
System.out.println(x == y); // false(更意外?)
这类问题在日常写代码中非常常见,理解清楚这两者的机制至关重要。
==
和equals
的区别==
运算符==
对于基本数据类型和引用类型的作用效果是不同的:
int
, char
, boolean
等):比较它们的值是否相等。String
, 自定义类等):比较两个对象的内存地址(即是否为同一个对象)。由于 Java 只有值传递,所以对于
==
来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
equals()
方法equals()
不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()
方法存在于Object
类中,而Object
类是所有类的直接或间接父类,因此所有的类都有equals()
方法。
Object
类中的equals
方法:
public boolean equals(Object obj) {
return (this == obj);
}
Object
类的 equals()
方法内部使用 ==
,比较对象的内存地址。String
、Integer
等类已重写 equals()
方法。特性 | == |
equals() |
---|---|---|
比较对象 | 基本类型值或对象的内存地址 | 对象的内容(需重写方法) |
默认行为 | 比较地址 | Object 类默认比较地址 |
可自定义 | 不可变 | 可重写以定义逻辑相等 |
String s = "abc"
)会进入字符串常量池,相同内容的字符串指向同一对象。new String("abc")
会创建新对象,即使内容相同,==
也会返回 false
。详见深入理解Java包装类:自动装箱拆箱与缓存池机制
Integer
、Long
等包装类在 -128
到 127
范围内会缓存对象,此时 ==
可能返回 true
:
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true(缓存范围内)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false(超出缓存范围)
equals()
equals()
,必须同时重写 hashCode()
,确保两个相等的对象返回相同的哈希码。HashMap
、HashSet
等哈希表时会出现逻辑错误。关于hashCode()
的详细内容可以查看:hashCode() 有什么用?
在 Java 中,数组是一种特殊的对象类型,它用于存储固定数量的同类型数据。它继承自 Object
类,且没有重写equals()
,因此:==
比较两个数组的内存地址(是否为同一个对象),equals()
使用的是Object
中的默认实现,效果等同于==
。
内容正确比较方式:
使用 Arrays.equals()
方法(一维数组)或 Arrays.deepEquals()
(多维数组)
import java.util.Arrays;
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // true(内容相同)
int[][] arr1 = {{1, 2}, {3, 4}};
int[][] arr2 = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepEquals(arr1, arr2)); // true
基本类型:直接使用 ==
比较值。
int a = 10, b = 10;
System.out.println(a == b); // true
对象类型:
==
。equals()
(需确保已正确重写)。优先使用 equals()
:避免依赖字符串常量池。
String s1 = new String("hello");
String s2 = "hello";
System.out.println(s1.equals(s2)); // true
空安全比较:将常量放在 equals()
左侧。
String str = null;
System.out.println("hello".equals(str)); // false(不会抛异常)
必须重写 equals()
和 hashCode()
:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyClass obj = (MyClass) o;
return Objects.equals(field1, obj.field1) &&
Objects.equals(field2, obj.field2);
}
@Override
public int hashCode() {
return Objects.hash(field1, field2);
}
使用 Arrays.equals()
或 Arrays.deepEquals()
:
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
System.out.println(Arrays.equals(arr1, arr2)); // true
使用 Objects.equals()
:
String s1 = null;
String s2 = "abc";
System.out.println(Objects.equals(s1, s2)); // false
直接使用 equals()
:避免依赖缓存机制。
Integer a = 200, b = 200;
System.out.println(a.equals(b)); // true
==
:判断“物理相等”(基本类型值或对象地址)。equals()
:判断“逻辑相等”(对象内容,需重写方法)。equals()
和 hashCode()
。