Object 类位于 java.lang 包中,由于它是一个核心类,其源码实现是 native 的,也就是说,它的方法是由底层的 C/C++ 代码实现的,而不是 Java 代码本身,我们无法直接看到其 C/C++ 的实现,但我们可以通过 Java 的文档和其行为来理解每一个方法的作用。
下面,我将详细解读 Object 类中的 11 个公共方法。
Object.java 源码结构概览
package java.lang;
public class Object {
// 1. native 方法:本地方法,由 JVM 实现
private static native void registerNatives();
static {
registerNatives();
}
// 2. 构造方法
public Object() {}
// 3. 核心方法
public final native Class<?> getClass();
public native int hashCode();
public boolean equals(Object obj) { ... }
protected native Object clone() throws CloneNotSupportedException;
public native String toString();
// 4. 线程相关
public final native void notify();
public final native void notifyAll();
public final void wait() throws InterruptedException { ... }
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void wait(long timeoutMillis, int nanos) throws InterruptedException { ... }
// 5. 垃圾回收相关
protected void finalize() throws Throwable { ... }
}
方法详解
public Object()
这是 Object 类的公共无参构造方法,由于所有类都继承自 Object,所以当你创建任何一个类的实例时,在构造函数的调用链的最底层,都会默认调用这个 Object() 构造方法。
// 下面这个类的构造过程隐式地调用了 Object()
public class MyClass {
public MyClass() {
// super(); // 这行代码是编译器自动添加的,调用父类 Object 的构造方法
}
}
public final native Class<?> getClass()
native: 表示这个方法不是由 Java 代码实现的,而是由 JVM 的本地方法实现。final: 表示这个方法不能被任何子类重写。- 作用: 返回此
Object运行时的类。Class类是一个反射 API 的入口,包含了类的完整信息,如类名、父类、接口、方法、字段等。 - 返回值: 一个
Class对象,代表该对象的实际类型。 - 示例:
String str = "Hello"; Class<?> clazz = str.getClass(); // clazz 的类型是 java.lang.Class<String> System.out.println(clazz.getName()); // 输出: java.lang.String
public native int hashCode()
native: 由 JVM 实现。- 作用: 返回该对象的哈希码值,哈希码主要用于基于哈希的集合,如
HashMap,HashSet,Hashtable等,用于确定对象在哈希表中的存储位置。 - 重要约定:
- 一致性: 在程序执行期间,只要对象的
equals方法所用的信息没有被修改,那么对同一个对象多次调用hashCode方法,必须返回相同的整数,但在同一应用程序的多次执行中,这个整数可以不同。 - 相等的对象必须有相同的哈希码:
a.equals(b)为true,a.hashCode()必须等于b.hashCode()。 - 不相等的对象,哈希码不一定不同:
a.equals(b)为false,a.hashCode()和b.hashCode()可以相同,这被称为“哈希碰撞”,哈希表通过链地址法或开放地址法来处理碰撞。
- 一致性: 在程序执行期间,只要对象的
- 默认实现: JVM 默认的实现是基于对象的内存地址来生成一个哈希码,两个不同的对象(即使内容相同),其哈希码也大概率是不同的。
- 重写: 当你重写
equals方法时,必须同时重写hashCode方法,以遵守上述约定,否则,对象将无法正确地在哈希集合中工作。
public boolean equals(Object obj)
- 作用: 判断两个对象是否“相等”,这个“相等”指的是逻辑上的相等,而不是内存地址的相同。
- 默认实现: 默认的
equals方法通过比较两个对象的内存地址来判断是否相等,即this == obj,这意味着只有两个引用指向同一个堆内存中的对象时,equals才返回true。 - 重写规则: 当你需要自定义对象的“相等”逻辑时(比较
Person对象的id是否相同),应该重写此方法,重写时需遵循以下约定:- 自反性:
x.equals(x)必须返回true。 - 对称性:
x.equals(y)返回true,y.equals(x)也必须返回true。 - 传递性:
x.equals(y)返回true,且y.equals(z)返回true,x.equals(z)也必须返回true。 - 一致性: 多次调用
x.equals(y),只要对象内容未变,结果必须一致。 - 非空性:
x.equals(null)必须返回false。
- 自反性:
- 最佳实践重写模板:
@Override public boolean equals(Object o) { // 1. 检查是否是同一个对象 if (this == o) return true; // 2. 检查是否为 null 或类型是否匹配 if (o == null || getClass() != o.getClass()) return false; // 3. 类型转换 MyClass myClass = (MyClass) o; // 4. 比较关键字段(通常先比较基本类型或非null对象) return myClass.id == this.id && Objects.equals(this.name, myClass.name); }
protected native Object clone() throws CloneNotSupportedException
native: 由 JVM 实现。- 作用: 创建并返回此对象的一个副本(拷贝)。
- 浅拷贝 vs 深拷贝:
- 浅拷贝:
clone()方法默认执行的是浅拷贝,它会创建一个新的对象,并将原对象中所有基本类型字段的值复制到新对象中,对于引用类型字段,它只复制引用(地址),而不是引用所指向的对象,新对象和原对象的引用类型字段指向的是同一个内存地址。 - 深拷贝: 如果需要实现深拷贝,你必须重写
clone()方法,并在其中递归地拷贝所有引用指向的对象。
- 浅拷贝:
Cloneable接口:Object类的clone()方法是受保护的,如果一个对象想要被克隆,它的类必须实现java.lang.Cloneable接口,如果一个类没有实现Cloneable接口,调用其clone()方法会抛出CloneNotSupportedException异常。- 使用场景: 克隆模式在实际开发中争议较大,因为它可能会破坏封装性,并且代码容易出错,通常推荐使用拷贝构造器或工厂模式来替代。
public String toString()
- 作用: 返回一个表示该对象的字符串,这个字符串通常应该是一个“简洁但信息丰富”的表示,便于调试和日志记录。
- 默认实现: 返回一个格式为
“类名@十六进制哈希码”的字符串。com.example.MyClass@1a2b3c4d。 - 重写: 几乎所有有意义的类都应该重写
toString()方法,以便提供更有意义的对象信息。 - 示例:
@Override public String toString() { return "MyClass{" + "id=" + id + ", name='" + name + '\'' + '}'; }
线程通信方法 (notify, notifyAll, wait)
这些方法用于多线程环境下的线程间通信,它们都作用于对象级别的锁(monitor)上。
public final native void notify():唤醒在此对象监视器上等待的单个线程,选择哪个线程是随意的,由 JVM 决定。
public final native void notifyAll():- 唤醒在此对象监视器上等待的所有线程。
public final native void wait(long timeoutMillis)和public final void wait(long timeoutMillis, int nanos):- 让当前线程等待,直到其他线程调用此对象的
notify()或notifyAll()方法,或者超过了指定的等待时间。
- 让当前线程等待,直到其他线程调用此对象的
public final void wait():- 让当前线程无限期等待,直到其他线程调用
notify()或notifyAll()。
- 让当前线程无限期等待,直到其他线程调用
- 重要: 这些方法只能在同步代码块或同步方法中调用,否则会抛出
IllegalMonitorStateException异常,它们是 Java 对象锁机制的核心。
protected void finalize() throws Throwable
- 作用: 这个方法被称为终结方法,当垃圾回收器确定没有对该对象的更多引用时,由垃圾回收器调用此方法。
- 注意:
finalize()方法非常不可靠,并且在 Java 9 之后被标记为deprecated(不推荐使用),原因如下:- 执行时机不确定: JVM 不保证
finalize()何时被调用,甚至在程序退出时可能都不会被调用。 - 性能开销:
finalize会影响垃圾回收的性能。 - 不保证执行:
finalize方法中抛出了未捕获的异常,垃圾回收过程会终止,且该对象可能永远不会被回收。
- 执行时机不确定: JVM 不保证
- 替代方案: 如果需要资源清理(如关闭文件、数据库连接等),应该使用
try-finally块或try-with-resources语句来确保资源被正确释放。finalize仅作为在 Java 早期版本中处理资源的一种补充,现已不推荐。
| 方法 | 修饰符 | 作用 | 是否可重写 | 备注 |
|---|---|---|---|---|
getClass() |
public final native |
获取对象的运行时类 | 否 | 返回 Class 对象,是反射的入口 |
hashCode() |
public native |
获取对象的哈希码 | 是 | 重写 equals 时必须重写此方法 |
equals() |
public |
判断两个对象是否逻辑相等 | 是 | 默认比较内存地址,需遵循约定重写 |
clone() |
protected native |
创建对象副本 | 是 | 需实现 Cloneable 接口,默认为浅拷贝 |
toString() |
public |
返回对象的字符串表示 | 是 | 默认返回 类名@哈希码,建议重写 |
notify() |
public final native |
唤醒一个等待线程 | 否 | 多线程通信,需在同步代码块中调用 |
notifyAll() |
public final native |
唤醒所有等待线程 | 否 | 多线程通信,需在同步代码块中调用 |
wait() |
public final |
使当前线程等待 | 否 | 多线程通信,需在同步代码块中调用 |
finalize() |
protected |
对象被回收时调用 | 是 | 已废弃,不推荐使用 |
掌握 Object 类的这些方法,特别是 equals, hashCode, 和 toString 的正确使用,是成为一名合格 Java 程序员的关键一步,它们是 Java 面向对象和集合框架的基石。
