Object 类核心作用
Object 是所有类的祖先
在 Java 里,每一个类都直接或间接继承自 Object。没有显式声明父类的类,默认就是 Object 的子类。
java
// 这两种写法等价
class Person { }
class Person extends Object { }Object 定义了所有对象都应该具备的基本能力——它就像 Java 世界的基本法。
Object 的核心方法
java
public class Object {
// 对象标识
public final Class<?> getClass(); // 获取运行时类
public native int hashCode(); // 哈希码
protected native Object clone(); // 克隆对象
// 相等性
public boolean equals(Object obj); // 判断相等
protected void finalize(); // 垃圾回收回调(已废弃)
// 字符串表示
public String toString(); // 对象的字符串表示
// 线程通信
public final void wait(); // 等待
public final void notify(); // 唤醒一个
public final void notifyAll(); // 唤醒全部
}equals:判断对象相等
equals 默认用 == 比较地址——两个引用是否指向同一个对象。
但对于「值相等」的需求(比如两个名字相同的人被认为是同一个人),需要重写:
java
class Person {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true; // 地址相等必然相等
if (o == null || getClass() != o.getClass()) return false; // 类型检查
Person other = (Person) o;
return Objects.equals(name, other.name); // 属性比较
}
}equals 契约(五条规则)
| 规则 | 含义 |
|---|---|
| 自反性 | x.equals(x) 必须返回 true |
| 对称性 | x.equals(y) 和 y.equals(x) 结果相同 |
| 传递性 | x.equals(y) && y.equals(z) → x.equals(z) |
| 一致性 | 多次调用结果一致 |
| 非空性 | x.equals(null) 返回 false |
hashCode:对象的哈希码
hashCode 返回一个整数,用于哈希表(HashMap、HashSet)的快速查找。
核心约定
如果
equals返回 true,两个对象的hashCode必须相同。
java
// 违反了约定——equals 相等但 hashCode 不同
class Broken {
private int value;
@Override
public boolean equals(Object o) {
return this.value == ((Broken) o).value;
}
@Override
public int hashCode() {
return new Random().nextInt(); // 每次都不同!违反约定!
}
}正确的 hashCode
java
class Person {
private String name;
private int age;
@Override
public int hashCode() {
return Objects.hash(name, age); // 简洁
// 或者手动实现
// int result = name != null ? name.hashCode() : 0;
// result = 31 * result + age;
// return result;
}
}为什么用 31?它是个奇数且不太大,乘法和位移能高效计算,同时哈希分布比较均匀。
toString:对象的字符串表示
默认实现返回「类名@哈希码的十六进制」:
java
Person p = new Person("张三", 25);
System.out.println(p); // Person@15db9742 —— 这是什么鬼?重写后:
java
class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
System.out.println(p); // Person{name='张三', age=25} —— 清晰多了getClass:获取运行时类型
java
Object obj = new Person("张三", 25);
Class<?> clazz = obj.getClass();
System.out.println(clazz.getName()); // com.example.Person
System.out.println(clazz.getSimpleName()); // Person注意:getClass() 不能被重写,它返回的是实际的运行时类型。
clone:对象克隆
clone 创建对象的副本,但需要实现 Cloneable 接口:
java
class Address implements Cloneable {
private String city;
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone(); // 浅拷贝
}
}
Address original = new Address("北京");
Address copy = original.clone(); // copy 是新的对象,但 city 引用相同深拷贝需要手动复制引用类型字段。
wait / notify:线程通信
这三个方法是 Java 线程间通信的基础,必须在 synchronized 块中使用:
java
synchronized (lock) {
while (!condition) {
lock.wait(); // 等待,释放锁
}
// 做事情...
lock.notify(); // 通知一个等待线程
// 或者 lock.notifyAll(); 通知所有
}典型应用:生产者-消费者问题。
finalize:已被废弃
finalize() 在对象被 GC 前调用,但 JDK 9 正式废弃它。
原因是:调用时间不确定、影响 GC 性能、可能造成资源泄漏。
不要使用 finalize,用 try-with-resources 或 AutoCloseable 替代。
总结
Object 的方法分三类:
| 类别 | 方法 | 说明 |
|---|---|---|
| 标识 | getClass() | 不能重写,返回运行时类型 |
| 相等性 | equals(), hashCode() | 通常需要一起重写 |
| 字符串 | toString() | 推荐重写,便于调试 |
| 克隆 | clone() | 需要 Cloneable,性能差 |
| 线程 | wait(), notify(), notifyAll() | 线程间通信 |
| 废弃 | finalize() | 不要使用 |
