十五、Object 类
文章目录
- Object 类
- 6.1 public Object()
- 6.2 toString方法
- 6.3 hashCode和equals(Object)
- 6.4 getClass方法
- 6.5 clone方法
- 6.6 finalize方法
Object 类
本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记
java.lang.Object
类是类层次结构的根类,每个类(除了Object
类本身)都使用Object
类作为超类。一个类如果没有显式声明继承另一个类,则相当于默认继承了Object
类。换句话说,Object
类的变量可以接收任意类型的对象。Java
规定Object[]
可以接收任意类型对象的数组,但是不能接收基本数据类型的数组。
package com.ieening.learnCommonApi;public class TestObject {public static void main(String[] args) {Object foo = new Object();Object stringObj = "小姑";Object tesObject = new TestObject();Object arrayObject = new int[5]; // 编译通过,此时把数组对象当成一个普通对象赋值给arrayObjectObject[] objects = new Object[3];Object[] strings = new String[3];Object[] intArray = new int[3]; // 编译报错:Type mismatch: cannot convert from int[] to Object[]}
}
Object
类是其他类的根父类,因此Object
类的所有方法都会继承到子类中,包括数组对象,了解Object
类的方法就非常重要。Object
类中的主要方法如下所示。
序号 | 方法名 | 描述 |
---|---|---|
1 | public Object() | 无参构造器 |
2 | public final native Class<?> getClass() | 返回对象运行时的类型 |
3 | public native int hashCode() | 获取对象的 hash 值 |
4 | public boolean equals(Object obj) | 比较两个对象的内容是否相等 |
5 | protected native Object clone() throws CloneNotSupportedException | 创建并返回对象的一个副本 |
6 | public String toString() | 对象字符串表示 |
7 | public final native void notify() | 唤醒当前对象监视器下等待的单个线程 |
8 | public final native void notifyAll() | 唤醒当前对象监视器下等待的所有线程 |
9 | public final void wait() throws InterruptedException | 当前线程等待直到被唤醒 |
10 | public final void wait(long timeoutMillis) throws InterruptedException | 当前线程等待直到被唤醒或时间结束 |
11 | public final void wait(long timeoutMillis, int nanos) throws InterruptedException | 当前线程等待直到被唤醒或时间结束 |
12 | protected void finalize() throws Throwable | 回收当前对象时,做一些清除操作 |
6.1 public Object()
Object
类只有一个默认的空参构造器,所有类的对象创建最终都会通过super()
语句调用到Object
类的无参构造器中。如果一个类没有显式继承另一个类,那么在它的构造器中出现的super()
语句表示调用的就是Object
类的无参构造器。
6.2 toString方法
toString
方法的作用是返回对象的字符串形式,也就是任意类型对象想转换成String
类型,都可以调用toString
方法。toString
方法的原型返回的是一个类似地址值的字符串,不够简明并且对于开发人员来讲该字符串的信息没有意义,所以建议子类在重写该方法时,返回一个简明易懂的信息表达式,一般为对象的属性信息。
/*** Returns a string representation of the object.* @apiNote* In general, the* {@code toString} method returns a string that* "textually represents" this object. The result should* be a concise but informative representation that is easy for a* person to read.* It is recommended that all subclasses override this method.* The string output is not necessarily stable over time or across* JVM invocations.* @implSpec* The {@code toString} method for class {@code Object}* returns a string consisting of the name of the class of which the* object is an instance, the at-sign character `{@code @}', and* the unsigned hexadecimal representation of the hash code of the* object. In other words, this method returns a string equal to the* value of:* <blockquote>* <pre>* getClass().getName() + '@' + Integer.toHexString(hashCode())* </pre></blockquote>** @return a string representation of the object.*/public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
6.3 hashCode和equals(Object)
1、hashCode
/*** Returns a hash code value for the object. This method is* supported for the benefit of hash tables such as those provided by* {@link java.util.HashMap}.* <p>* The general contract of {@code hashCode} is:* <ul>* <li>Whenever it is invoked on the same object more than once during* an execution of a Java application, the {@code hashCode} method* must consistently return the same integer, provided no information* used in {@code equals} comparisons on the object is modified.* This integer need not remain consistent from one execution of an* application to another execution of the same application.* <li>If two objects are equal according to the {@link* #equals(Object) equals} method, then calling the {@code* hashCode} method on each of the two objects must produce the* same integer result.* <li>It is <em>not</em> required that if two objects are unequal* according to the {@link #equals(Object) equals} method, then* calling the {@code hashCode} method on each of the two objects* must produce distinct integer results. However, the programmer* should be aware that producing distinct integer results for* unequal objects may improve the performance of hash tables.* </ul>** @implSpec* As far as is reasonably practical, the {@code hashCode} method defined* by class {@code Object} returns distinct integers for distinct objects.** @return a hash code value for this object.* @see java.lang.Object#equals(java.lang.Object)* @see java.lang.System#identityHashCode*/@IntrinsicCandidatepublic native int hashCode();
hashCode
方法的说明有以下几点:
hashCode
方法用于返回对象的哈希码值。支持此方法是为了提高哈希表(如java.util.Hashtable
提供的哈希表)的性能。hashCode
在Object
类中有native
修饰,是本地方法,该方法的方法体不是Java
实现的,是由C/C++
实现的,最后编译为.dll
文件,然后由Java
调用。
hashCode
方法重写时要满足如下几个要求。
- 如果两个对象调用
equals
方法返回true
,那么要求这两个对象的hashCode
值一定是相等的。 - 如果两个对象的
hashCode
值不相等,那么要求这两个对象调用equals
方法一定是false
。 - 如果两个对象的
hashCode
值相等,那么这两个对象调用equals
方法可能是true
,也可能是false
。
2、equals(Object)
判断引用相等使用==
,两个对象内容相等使用equals
,下面是Object equals
代码:
/*** Indicates whether some other object is "equal to" this one.* <p>* The {@code equals} method implements an equivalence relation* on non-null object references:* <ul>* <li>It is <i>reflexive</i>: for any non-null reference value* {@code x}, {@code x.equals(x)} should return* {@code true}.* <li>It is <i>symmetric</i>: for any non-null reference values* {@code x} and {@code y}, {@code x.equals(y)}* should return {@code true} if and only if* {@code y.equals(x)} returns {@code true}.* <li>It is <i>transitive</i>: for any non-null reference values* {@code x}, {@code y}, and {@code z}, if* {@code x.equals(y)} returns {@code true} and* {@code y.equals(z)} returns {@code true}, then* {@code x.equals(z)} should return {@code true}.* <li>It is <i>consistent</i>: for any non-null reference values* {@code x} and {@code y}, multiple invocations of* {@code x.equals(y)} consistently return {@code true}* or consistently return {@code false}, provided no* information used in {@code equals} comparisons on the* objects is modified.* <li>For any non-null reference value {@code x},* {@code x.equals(null)} should return {@code false}.* </ul>** <p>* An equivalence relation partitions the elements it operates on* into <i>equivalence classes</i>; all the members of an* equivalence class are equal to each other. Members of an* equivalence class are substitutable for each other, at least* for some purposes.** @implSpec* The {@code equals} method for class {@code Object} implements* the most discriminating possible equivalence relation on objects;* that is, for any non-null reference values {@code x} and* {@code y}, this method returns {@code true} if and only* if {@code x} and {@code y} refer to the same object* ({@code x == y} has the value {@code true}).** In other words, under the reference equality equivalence* relation, each equivalence class only has a single element.** @apiNote* It is generally necessary to override the {@link #hashCode() hashCode}* method whenever this method is overridden, so as to maintain the* general contract for the {@code hashCode} method, which states* that equal objects must have equal hash codes.** @param obj the reference object with which to compare.* @return {@code true} if this object is the same as the obj* argument; {@code false} otherwise.* @see #hashCode()* @see java.util.HashMap*/public boolean equals(Object obj) {return (this == obj);}
从代码可以知道,Object
的equals
与==
的效果是一样的。我们希望判断的是两个对象的属性内容是否相等,所以往往需要重写equals
方法。
重写equals
方法时,代码主要分为三个方面:
- 两个对象的地址一样,肯定返回
true
。 - 两个对象的类型不一样,肯定返回
false
。 - 两个对象被选择比较的属性信息完全一样,肯定返回
true
,有不一样的则返回false
。
关于equals
方法的重写,Java
规定一定要遵循如下几个原则。
- 自反性:
x.equals(x)
返回true
。 - 传递性:
x.equals(y)
返回true
,y.equals(z)
返回true
,则x.equals(z)
也应该返回true
。 - 一致性:只要参与
equals
方法比较的属性值没有修改,那么无论何时调用equals
方法的结果应该都是一致的。 - 对称性:
x.equals(y)
与y.equals(x)
的结果应该一致。 非空对象.equals(null)
的结果一定是false
。
关于==
和equals
方法的区别,总结如下。
==
可用于判断两个基本数据类型变量,也可以用于判断两个引用类型变量。但都需要保证判断双方的类型一致或兼容,否则编译报错。equals
方法只能用于判断引用类型的变量,因为只有对象才有方法,默认判断的是对象的内容,如果重写Object
类的equals
方法,则一般判断的是对象的内容是否相等。
6.4 getClass方法
我们知道对象有静态类型(编译时类型)和动态类型(运行时类型),静态类型和动态类型可能不一样。静态类型比较好判断,就是变量声明时的类型,那么动态类型呢?动态类型需要使用getClass
方法。Object getClass
代码方法如下:
/*** Returns the runtime class of this {@code Object}. The returned* {@code Class} object is the object that is locked by {@code* static synchronized} methods of the represented class.** <p><b>The actual result type is {@code Class<? extends |X|>}* where {@code |X|} is the erasure of the static type of the* expression on which {@code getClass} is called.</b> For* example, no cast is required in this code fragment:</p>** <p>* {@code Number n = 0; }<br>* {@code Class<? extends Number> c = n.getClass(); }* </p>** @return The {@code Class} object that represents the runtime* class of this object.* @jls 15.8.2 Class Literals*/@IntrinsicCandidatepublic final native Class<?> getClass();
6.5 clone方法
如果需要复制一个对象,则可以使用Object
类提供的clone
方法。该方法在Object
类中的源码如下所示:
/*** Creates and returns a copy of this object. The precise meaning* of "copy" may depend on the class of the object. The general* intent is that, for any object {@code x}, the expression:* <blockquote>* <pre>* x.clone() != x</pre></blockquote>* will be true, and that the expression:* <blockquote>* <pre>* x.clone().getClass() == x.getClass()</pre></blockquote>* will be {@code true}, but these are not absolute requirements.* While it is typically the case that:* <blockquote>* <pre>* x.clone().equals(x)</pre></blockquote>* will be {@code true}, this is not an absolute requirement.* <p>* By convention, the returned object should be obtained by calling* {@code super.clone}. If a class and all of its superclasses (except* {@code Object}) obey this convention, it will be the case that* {@code x.clone().getClass() == x.getClass()}.* <p>* By convention, the object returned by this method should be independent* of this object (which is being cloned). To achieve this independence,* it may be necessary to modify one or more fields of the object returned* by {@code super.clone} before returning it. Typically, this means* copying any mutable objects that comprise the internal "deep structure"* of the object being cloned and replacing the references to these* objects with references to the copies. If a class contains only* primitive fields or references to immutable objects, then it is usually* the case that no fields in the object returned by {@code super.clone}* need to be modified.** @implSpec* The method {@code clone} for class {@code Object} performs a* specific cloning operation. First, if the class of this object does* not implement the interface {@code Cloneable}, then a* {@code CloneNotSupportedException} is thrown. Note that all arrays* are considered to implement the interface {@code Cloneable} and that* the return type of the {@code clone} method of an array type {@code T[]}* is {@code T[]} where T is any reference or primitive type.* Otherwise, this method creates a new instance of the class of this* object and initializes all its fields with exactly the contents of* the corresponding fields of this object, as if by assignment; the* contents of the fields are not themselves cloned. Thus, this method* performs a "shallow copy" of this object, not a "deep copy" operation.* <p>* The class {@code Object} does not itself implement the interface* {@code Cloneable}, so calling the {@code clone} method on an object* whose class is {@code Object} will result in throwing an* exception at run time.** @return a clone of this instance.* @throws CloneNotSupportedException if the object's class does not* support the {@code Cloneable} interface. Subclasses* that override the {@code clone} method can also* throw this exception to indicate that an instance cannot* be cloned.* @see java.lang.Cloneable*/@IntrinsicCandidateprotected native Object clone() throws CloneNotSupportedException;
调用该方法时可以创建并返回当前对象的一个副本。从源码中可以发现该方法的权限修饰符是protected
,说明默认Object
类中的clone
方法只能在java.lang
包或其他包的子类中调用。因此,如果在测试类中要通过自定义类的对象来调用clone
方法,则必须重写该方法。这里要注意的是,如果要重写该方法,则子类必须实现java.lang.Cloneable
接口,否则会抛出CloneNotSupportedException
。
6.6 finalize方法
Object
类中finalize
方法的源码如下所示:
/*** Called by the garbage collector on an object when garbage collection* determines that there are no more references to the object.* A subclass overrides the {@code finalize} method to dispose of* system resources or to perform other cleanup.* <p>* <b>When running in a Java virtual machine in which finalization has been* disabled or removed, the garbage collector will never call* {@code finalize()}. In a Java virtual machine in which finalization is* enabled, the garbage collector might call {@code finalize} only after an* indefinite delay.</b>* <p>* The general contract of {@code finalize} is that it is invoked* if and when the Java virtual* machine has determined that there is no longer any* means by which this object can be accessed by any thread that has* not yet died, except as a result of an action taken by the* finalization of some other object or class which is ready to be* finalized. The {@code finalize} method may take any action, including* making this object available again to other threads; the usual purpose* of {@code finalize}, however, is to perform cleanup actions before* the object is irrevocably discarded. For example, the finalize method* for an object that represents an input/output connection might perform* explicit I/O transactions to break the connection before the object is* permanently discarded.* <p>* The {@code finalize} method of class {@code Object} performs no* special action; it simply returns normally. Subclasses of* {@code Object} may override this definition.* <p>* The Java programming language does not guarantee which thread will* invoke the {@code finalize} method for any given object. It is* guaranteed, however, that the thread that invokes finalize will not* be holding any user-visible synchronization locks when finalize is* invoked. If an uncaught exception is thrown by the finalize method,* the exception is ignored and finalization of that object terminates.* <p>* After the {@code finalize} method has been invoked for an object, no* further action is taken until the Java virtual machine has again* determined that there is no longer any means by which this object can* be accessed by any thread that has not yet died, including possible* actions by other objects or classes which are ready to be finalized,* at which point the object may be discarded.* <p>* The {@code finalize} method is never invoked more than once by a Java* virtual machine for any given object.* <p>* Any exception thrown by the {@code finalize} method causes* the finalization of this object to be halted, but is otherwise* ignored.** @apiNote* Classes that embed non-heap resources have many options* for cleanup of those resources. The class must ensure that the* lifetime of each instance is longer than that of any resource it embeds.* {@link java.lang.ref.Reference#reachabilityFence} can be used to ensure that* objects remain reachable while resources embedded in the object are in use.* <p>* A subclass should avoid overriding the {@code finalize} method* unless the subclass embeds non-heap resources that must be cleaned up* before the instance is collected.* Finalizer invocations are not automatically chained, unlike constructors.* If a subclass overrides {@code finalize} it must invoke the superclass* finalizer explicitly.* To guard against exceptions prematurely terminating the finalize chain,* the subclass should use a {@code try-finally} block to ensure* {@code super.finalize()} is always invoked. For example,* <pre>{@code @Override* protected void finalize() throws Throwable {* try {* ... // cleanup subclass state* } finally {* super.finalize();* }* }* }</pre>** @deprecated Finalization is deprecated and subject to removal in a future* release. The use of finalization can lead to problems with security,* performance, and reliability.* See <a href="https://openjdk.org/jeps/421">JEP 421</a> for* discussion and alternatives.* <p>* Subclasses that override {@code finalize} to perform cleanup should use* alternative cleanup mechanisms and remove the {@code finalize} method.* Use {@link java.lang.ref.Cleaner} and* {@link java.lang.ref.PhantomReference} as safer ways to release resources* when an object becomes unreachable. Alternatively, add a {@code close}* method to explicitly release resources, and implement* {@code AutoCloseable} to enable use of the {@code try}-with-resources* statement.* <p>* This method will remain in place until finalizers have been removed from* most existing code.** @throws Throwable the {@code Exception} raised by this method* @see java.lang.ref.WeakReference* @see java.lang.ref.PhantomReference* @jls 12.6 Finalization of Class Instances*/@Deprecated(since="9", forRemoval=true)protected void finalize() throws Throwable { }
finalize
方法是Object
类中的protected
方法,子类可以重写该方法以实现资源清理工作,GC
在回收对象之前会调用该方法,即该方法不是由开发人员手动调用的。当对象变成不可达时,即对象成为需要被回收的垃圾对象时,GC
会判断该对象是否覆盖了finalize
方法,若未覆盖,则直接将其回收。若对象未执行过finalize
方法,则将其放入F-Queue
队列,由一个低优先级线程执行该队列中对象的finalize
方法。执行完finalize
方法后,GC
会再次判断该对象是否可达,若不可达,则进行回收,否则对象复活,复活后的对象下次回收时,将不再放入F-Queue
队列,即不再执行其finalize
方法。Java
语言规范并不能保证finalize
方法会被及时执行,而且根本不能保证它们会被执行。所以不建议用finalize
方法完成非内存资源清理工作以外的任务。
马俊昌.Java编程的逻辑[M].北京:机械工业出版社,2018. ↩︎
尚硅谷教育.剑指Java:核心原理与应用实践[M].北京:电子工业出版社,2023. ↩︎