文章目录
- 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]; // 编译通过,此时把数组对象当成一个普通对象赋值给arrayObject
Object[] 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
*/
@IntrinsicCandidate
public 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
*/
@IntrinsicCandidate
public 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
*/
@IntrinsicCandidate
protected 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. ↩︎