Java Object 类

背景与动机#

在 Java 里,几乎所有类最终都直接或间接继承自 Object。这意味着很多看似“天生就有”的方法,其实都来自 Object

Object 的意义,不只是记住它有哪些方法,而是搞清楚 Java 里“对象是什么”“对象怎么比较”“对象怎么打印”这些最基础的问题。

核心原理拆解#

1. Object 是所有类的根类#

class User {
}

虽然这里没有显式写 extends Object,但它仍然等价于:

class User extends Object {
}

所以任何普通类对象,都至少会有 toStringequalshashCodegetClass 这些方法。

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); // false
System.out.println(a.equals(b)); // true

如果自定义类不重写 equals,默认行为和 == 很接近,比较的仍然是“是不是同一个对象”。

4. hashCode:给对象一个哈希值#

hashCode 常和 equals 一起出现,尤其是在 HashSetHashMap 这类哈希容器中。

要记住一条关键规则:

  • 如果两个对象 equals 结果为 true,它们的 hashCode 必须相同
  • 反过来不成立,hashCode 相同的对象不一定 equals

5. 为什么 equalshashCode 要一起重写#

如果只重写 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. 为什么 equalshashCode 要一起重写#

因为哈希容器依赖这两个方法一起工作。如果 equals 相等但 hashCode 不同,就可能出现查找不到、去重失败等问题。

一句话总结#

Object 决定了 Java 对象最基础的行为模型:对象怎么打印、怎么比较、怎么参与哈希容器,理解它之后,很多后续类库知识都会顺起来。

文章目录

文章目录