背景与动机
在 Java 里,几乎所有类最终都直接或间接继承自 Object。这意味着很多看似“天生就有”的方法,其实都来自 Object。
学 Object 的意义,不只是记住它有哪些方法,而是搞清楚 Java 里“对象是什么”“对象怎么比较”“对象怎么打印”这些最基础的问题。
核心原理拆解
1. Object 是所有类的根类
class User {}虽然这里没有显式写 extends Object,但它仍然等价于:
class User extends Object {}所以任何普通类对象,都至少会有 toString、equals、hashCode、getClass 这些方法。
2. toString:把对象转成字符串
Object obj = new Object();System.out.println(obj.toString());默认 toString 往往输出“类名 + @ + 十六进制哈希值”,不适合直接展示业务对象。工程里通常会重写它,方便日志和调试。
class User { String name;
@Override public String toString() { return "User{name='" + name + "'}"; }}3. equals:比较对象是否“逻辑相等”
== 比较的是两个引用是不是指向同一个对象,equals 比较的是两个对象在业务意义上是否相等。
String a = new String("java");String b = new String("java");
System.out.println(a == b); // falseSystem.out.println(a.equals(b)); // true如果自定义类不重写 equals,默认行为和 == 很接近,比较的仍然是“是不是同一个对象”。
4. hashCode:给对象一个哈希值
hashCode 常和 equals 一起出现,尤其是在 HashSet、HashMap 这类哈希容器中。
要记住一条关键规则:
- 如果两个对象
equals结果为true,它们的hashCode必须相同 - 反过来不成立,
hashCode相同的对象不一定equals
5. 为什么 equals 和 hashCode 要一起重写
如果只重写 equals 不重写 hashCode,对象放进哈希容器后,去重、查找行为就可能不稳定。
class User { String name;
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof User)) { return false; } User other = (User) obj; return java.util.Objects.equals(name, other.name); }
@Override public int hashCode() { return java.util.Objects.hash(name); }}6. getClass:拿到运行时真实类型
User user = new User();System.out.println(user.getClass().getName());getClass 常用于调试、反射和运行时类型判断。
7. clone 了解即可
Object 里还有一个 clone 方法,但它默认不是随便就能安全用的。实际项目里,很多团队更倾向于手动复制、拷贝构造方法,或借助序列化 / 工具库来实现对象复制。
入门阶段知道它存在即可,不必把它当成对象复制的首选方案。
最小可运行代码示例
public class ObjectDemo { public static void main(String[] args) { User u1 = new User("Tom"); User u2 = new User("Tom");
System.out.println(u1); System.out.println(u1 == u2); System.out.println(u1.equals(u2)); System.out.println(u1.hashCode() == u2.hashCode()); System.out.println(u1.getClass().getSimpleName()); }}
class User { private final String name;
User(String name) { this.name = name; }
@Override public String toString() { return "User{name='" + name + "'}"; }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof User)) { return false; } User other = (User) obj; return java.util.Objects.equals(name, other.name); }
@Override public int hashCode() { return java.util.Objects.hash(name); }}常见陷阱与错误示例
1. 误把 == 当成字符串内容比较
== 比较的是引用地址,不是内容。字符串内容比较应优先用 equals。
2. 只重写 equals,不重写 hashCode
这会破坏哈希容器对对象的一致性预期,导致 HashSet 去重、HashMap 查找结果异常。
3. toString 不重写,日志难看又难排查
默认 toString 对业务对象可读性较差。只要对象需要打印、调试、记录日志,就值得重写。
面试高频问题
1. 为什么说 Object 是所有类的根类
因为 Java 里的普通类默认都会直接或间接继承 Object,所以所有对象都天然拥有 Object 定义的通用方法。
2. == 和 equals 有什么区别
== 比较引用是否相同,equals 比较逻辑内容是否相等。对自定义类来说,是否“按内容相等”要看是否重写了 equals。
3. 为什么 equals 和 hashCode 要一起重写
因为哈希容器依赖这两个方法一起工作。如果 equals 相等但 hashCode 不同,就可能出现查找不到、去重失败等问题。
一句话总结
Object 决定了 Java 对象最基础的行为模型:对象怎么打印、怎么比较、怎么参与哈希容器,理解它之后,很多后续类库知识都会顺起来。