Object作为父类
Object类的方法
位于java.lang包中的Object类位于类层次结构树的顶端。所有类都是Object类的直接或间接子类。您使用或编写的每个类都继承了Object的实例方法。您无需使用这些方法,但若选择使用,则可能需要通过特定于您类的代码覆盖它们。本节讨论的从Object继承的方法包括:
- protected Object clone() throws CloneNotSupportedException: 创建并返回此对象的一个副本。
- public boolean equals(Object obj): 指示某个对象是否与此对象“相等”。
- protected void finalize() throws Throwable: 当垃圾回收器确定对象不再被引用时,由垃圾回收器调用该对象。从Java 18开始,该方法已被废弃并计划移除。
- public Class<?> getClass(): 返回此对象的运行时类。
- public int hashCode(): 返回该对象的哈希码值。
- public String toString(): 返回该对象的字符串表示形式。
请注意,自 Java SE 9 起,finalize() 方法已被弃用,并计划在 Java SE 18 中移除。强烈建议不要重写此方法。该方法终将不再被调用。更多信息请参阅本页末尾部分。
Object类的notify()、notifyAll()和wait()方法均参与同步程序中独立运行的线程活动,相关内容将在后续章节讨论,此处不再赘述。此类方法共有五种:
- public final void notify()
- public final void notifyAll()
- public final void wait()
- public final void wait(long timeout)
- public final void wait(long timeout, int nanos)
注:这些方法中存在若干微妙之处,尤其是克隆方法。
toString() 方法
你应该始终考虑在类中重写 toString() 方法。
对象的 toString() 方法返回该对象的字符串表示形式,这对调试非常有用。对象的字符串表示形式完全取决于对象本身,因此你需要在类中重写 toString() 方法。
你可以结合使用 toString() 和 IO.println() 来显示对象的文本表示形式,例如 Book 类的实例:
IO.println(firstBook.toString());
对于正确重写的 toString() 方法,它将输出有用的内容,例如:
ISBN: 0201914670; The Swing Tutorial; A Guide to Constructing GUIs, 2nd Edition
equals() 方法
equals()方法用于比较两个对象是否相等,若相等则返回true。Object类提供的equals()方法使用身份运算符(==)来判断两个对象是否相等。对于基本数据类型,这种方式能得到正确结果;但对于对象而言则不然。Object类提供的equals()方法测试的是对象引用是否相等——即被比较的对象是否完全相同。
要测试两个对象是否在等价意义上相等(包含相同信息),必须重写 equals() 方法。以下是一个重写 equals() 的 Book 类示例:
public class Book {
String ISBN;
public String getISBN() {
return ISBN;
}
public boolean equals(Object obj) {
if (obj instanceof Book)
return ISBN.equals((Book)obj.getISBN());
else
return false;
}
}
考虑以下代码,它用于测试两个 Book 类的实例是否相等:
// Swing Tutorial, 2nd edition
Book firstBook = new Book("0201914670");
Book secondBook = new Book("0201914670");
if (firstBook.equals(secondBook)) {
IO.println("objects are equal");
} else {
IO.println("objects are not equal");
}
该程序显示对象相等,尽管firstBook和secondBook引用了两个不同的对象。它们被视为相等,因为被比较的对象包含相同的ISBN号码。
如果 == 运算符不适用于您的类,则应始终重写 equals() 方法。
注意:如果重写了 equals() 方法,则必须同时重写 hashCode() 方法。
hashCode() 方法
hashCode()方法返回的值是该对象的哈希码,即由哈希算法生成的整数值。
根据定义,若两个对象相等,其hashcode也必须相等。若重写equals()方法,则改变了两个对象的相等判定方式,此时Object类实现的hashCode()方法将不再有效。因此,若重写equals()方法,必须同时重写hashCode()方法。
getClass() 方法
您无法覆盖 getClass() 方法。
getClass() 方法返回一个 Class 对象,该对象提供若干方法用于获取类的相关信息,例如类名(getSimpleName())、父类(getSuperclass())以及实现的接口(getInterfaces())。例如,以下方法可获取并显示对象的类名:
void printClassName(Object obj) {
IO.println("The object's" + " class is " +
obj.getClass().getSimpleName());
}
位于java.lang包中的Class类拥有大量方法(超过50个)。例如,你可以测试该类是否为注解(isAnnotation())、接口(isInterface())或枚举(isEnum())。你可以查看对象的字段(getFields())或方法(getMethods())等。
clone() 方法
如果某个类或其超类实现了Cloneable接口,则可使用clone()方法从现有对象创建副本。创建克隆对象时,需编写如下代码:
aCloneableObject.clone();
对象对该方法的实现会检查被调用 clone() 的对象是否实现了 Cloneable 接口。若对象未实现该接口,方法将抛出 CloneNotSupportedException 异常。异常处理将在“异常”章节中详述。目前您只需了解 clone() 必须声明为
protected Object clone() throws CloneNotSupportedException
或者
public Object clone() throws CloneNotSupportedException
如果你打算编写一个 clone() 方法来覆盖 Object 中的方法。
如果调用 clone() 的对象实现了 Cloneable 接口,则 Object 类对 clone() 方法的实现将创建一个与原始对象同类的对象,并将新对象的成员变量初始化为与原始对象对应成员变量相同的值。
使类可克隆的最简单方法是在类声明中添加 implements Cloneable。这样对象就能调用 clone() 方法。
对于某些类而言,Object类的clone()方法默认行为完全适用。但若对象包含对外部对象(如ObjExternal)的引用,则可能需要重写clone()方法以获得正确行为。否则,某个对象对 ObjExternal 的修改也会反映在其克隆体中。这意味着原始对象与其克隆体并非独立——要实现解耦,必须重写 clone() 方法,使其同时克隆对象本身和 ObjExternal。此时原始对象引用原始的 ObjExternal,克隆体则引用 ObjExternal 的克隆体,从而真正实现对象与其克隆体的独立性。
finalize() 方法
Object类提供了一个回调方法finalize(),当对象成为垃圾时可对其调用。Object类对finalize()的实现不执行任何操作。重写finalize()旨在执行清理工作,例如释放资源。
finalize() 方法可能被系统自动调用,但其调用时机甚至是否会被调用都无法确定。因此,您不应再依赖此方法来执行清理工作。例如,若在代码中执行 I/O 操作后未关闭文件描述符,却期望 finalize() 自动完成关闭,则可能导致文件描述符耗尽。
从 Java SE 9 开始,finalize() 方法已被弃用,而从 Java SE 18 开始,该方法将被移除。未来某个时刻,它将不再被调用。强烈建议不要重写此方法。若需清理某些资源,可通过实现 AutoCloseable 接口来实现。此内容在 Java I/O 章节中有详细说明。