RHODES ISLAND // PERSONAL ARCHIVE

关之水瑶 · 作战记录

记录技术、生活与正在进行的思考。欢迎来到我的个人情报终端。

SYSTEM ONLINE / 2026

5. 请说出 == 和 equals 方法的区别是什么?Object.equals 的默认行为是什么?

== 和 equals 的区别,以及 Object.equals 的默认行为

一句话回答

  • ==:基本类型比较值,引用类型比较内存地址(引用是否相同)。
  • equals:Object 的方法,默认行为和 == 一样,比较引用;但很多类(如 String、Integer)重写后比较内容/逻辑相等。
  • Object.equals 默认行为:return this == obj;,即判断是否是同一个对象。

1. == 的语义

1.1 基本类型:比较值

int a = 10;
int b = 10;
System.out.println(a == b); // true

double d1 = 1.0;
double d2 = 1.0;
System.out.println(d1 == d2); // true

对于 byte、short、int、long、float、double、char、boolean,== 比较的是数值本身。

1.2 引用类型:比较地址

String s1 = new String("hello");
String s2 = new String("hello");

System.out.println(s1 == s2);      // false,两个不同对象
System.out.println(s1.equals(s2)); // true,内容相同

== 比较的是两个引用是否指向同一个对象(堆中同一块内存地址)。

1.3 字符串常量池的影响

String a = "hello";
String b = "hello";
String c = new String("hello");

System.out.println(a == b);      // true,都指向常量池同一对象
System.out.println(a == c);      // false,c 是堆上新对象
System.out.println(a.equals(c)); // true,内容相同

== 为 true 不代表内容相同,只是恰好指向同一对象。
判断字符串内容,永远用 equals。


2. equals 的语义

2.1 Object.equals 默认行为

Object 是所有类的父类,其 equals 源码:

public boolean equals(Object obj) {
    return (this == obj);
}

默认行为就是 ==:判断两个引用是否指向同一个对象。

class Person {
    String name;
}

Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.equals(p2)); // false,默认比较地址

2.2 重写后的 equals

很多类重写了 equals,改为比较内容或业务逻辑:

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true,String 重写为比较字符内容

String.equals 源码简化:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        // 逐字符比较
        return ...;
    }
    return false;
}

2.3 包装类的 equals

Integer i1 = new Integer(100);
Integer i2 = new Integer(100);

System.out.println(i1 == i2);      // false,不同对象
System.out.println(i1.equals(i2)); // true,值相同

注意:Integer 缓存 -128 ~ 127,此范围内 == 可能为 true,超出则为 false,容易踩坑。
包装类比较值,一律用 equals,或先拆箱。

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true,缓存

Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false,超出缓存

3. 核心对比

维度 == equals
类型 运算符 方法
基本类型 比较值 不能用于基本类型
引用类型 比较地址 默认比较地址,可重写为比较内容
是否可重写 不能 可以
所属 语言层面 Object 定义,可覆盖
典型用途 判断是否同一对象 判断逻辑是否相等
性能 快 通常较慢,可能逐字段比较

记忆:
== 比地址,equals 比内容(前提是重写了)。
基本类型只能用 ==,引用类型判断内容用 equals。


4. 重写 equals 的规范

重写 equals 必须满足以下契约(来自 Object 文档):

  1. 自反性:x.equals(x) 必须为 true。
  2. 对称性:x.equals(y) 为 true,则 y.equals(x) 也必须为 true。
  3. 传递性:x.equals(y)、y.equals(z) 为 true,则 x.equals(z) 也必须为 true。
  4. 一致性:只要对象状态没变,多次调用结果一致。
  5. 非空性:x.equals(null) 必须为 false。

重写示例

public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
               Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

重写 equals 必须同时重写 hashCode

原因:

  • HashMap、HashSet 先用 hashCode 定位桶,再用 equals 比较。
  • 如果两个对象 equals 为 true,但 hashCode 不同,会违反约定,导致集合行为异常。

约定:

  1. equals 为 true,hashCode 必须相同。
  2. hashCode 相同,equals 不一定为 true。
Map<Person, String> map = new HashMap<>();
Person p1 = new Person("Tom", 18);
Person p2 = new Person("Tom", 18);

map.put(p1, "x");
System.out.println(map.get(p2)); // 若正确重写 equals 和 hashCode,输出 x

5. 常见坑

5.1 字符串比较用 ==

// 错误
if (input == "yes") { ... }

// 正确
if ("yes".equals(input)) { ... }

用 "yes".equals(input) 还能避免 input 为 null 时的 NPE。

5.2 包装类比较用 ==

Integer a = 128;
Integer b = 128;
System.out.println(a == b);      // false
System.out.println(a.equals(b)); // true

5.3 重写 equals 没重写 hashCode

Set<Person> set = new HashSet<>();
set.add(new Person("Tom", 18));
System.out.println(set.contains(new Person("Tom", 18))); // 可能 false

5.4 equals 参数类型写错

// 错误:参数类型不是 Object,变成重载而非重写
public boolean equals(Person o) { ... }

// 正确
@Override
public boolean equals(Object o) { ... }

加 @Override 可让编译器帮你检查。

5.5 对 null 调用 equals

String s = null;
s.equals("x");        // NPE
"x".equals(s);        // false,安全
Objects.equals(s, "x"); // false,安全

6. 面试口述版

== 是运算符,基本类型比较值,引用类型比较内存地址,也就是是否是同一个对象。

equals 是 Object 定义的方法,默认实现就是 this == obj,和 == 一样比较地址。但很多类重写了它,比如 String、Integer,改成比较内容或逻辑相等。

所以判断两个引用是否指向同一个对象用 ==,判断内容是否相等用 equals。字符串和包装类的值比较一定要用 equals,因为 == 可能因为常量池或缓存产生误导。

重写 equals 要满足自反、对称、传递、一致、非空五个契约,并且必须同时重写 hashCode,否则在 HashMap、HashSet 中会出现问题。


7. 常见追问

  • Object.equals 默认行为是什么?
    return this == obj;,比较引用是否相同。

  • 为什么重写 equals 必须重写 hashCode?
    保证 equals 相等的对象 hashCode 也相等,否则哈希集合行为异常。

  • String 为什么要重写 equals?
    字符串是值语义,业务上关心内容是否相同,而不是是否同一对象。

  • == 能比较两个 null 吗?
    能,null == null 为 true;但 null.equals(...) 会 NPE。

  • Integer 的 == 为什么有时是 true?
    Integer 缓存 -128 ~ 127,此范围内自动装箱复用同一对象。

  • equals 和 hashCode 的关系?
    equals 为 true 则 hashCode 必须相同;hashCode 相同,equals 不一定为 true。

  • Objects.equals 和 a.equals(b) 区别?
    Objects.equals 做了 null 安全判断,a 为 null 时返回 b == null,不会 NPE。

posted @ 2026-09-24 17:17  关之水瑶  阅读(11)  评论(0)    收藏  举报