日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java Object 类方法解析

發布時間:2023/12/15 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java Object 类方法解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文標題大綱:

文章目錄

    • 前言
        • getClass
        • hashCode
        • equals
        • clone
          • 深拷貝和淺拷貝
        • toString
        • wait / notify / notifyAll
        • finalize

前言

我們都知道 Java 語言是面向對象的編程語言,而面向對象編程以類作為基本單元。我們也都知道,在 Java 中,所有的類都將 Object 類作為父類,而 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*/ public final native Class<?> getClass();

這是一個 final 類型的 native 方法,也就是說這個方法不能被子類重寫,同時它的實現并不是通過 Java 語言實現的,而是用其他語言(C/C++)實現的,我們得到某個對象的類型信息(Class 類的對象)之后,我們就可以利用 Java 反射的機制做很多事情了,比如有以下代碼:

public class Main {static class People {String name;public People(String name) {this.name = name;}public void work() {System.out.println(name + " is working!");}}private static void startTest() {People p1 = new People("指點");p1.work();// 獲取 p1 對象的類型信息,在這里編譯器只知道 p1 是 People 類型的對象的引用,// 但是編譯器并不知道具體是 People 類型的對象還是 People 的子類對象(雖然在這里并沒有 People 的子類),// 所以我們這里用泛型必須加類型邊界限定符,// 當然也可以不加,這樣就代表這是一個任意類型的 Class 對象Class<? extends People> cp = p1.getClass();People p2 = null;try {// 獲取 cp 代表的 Class 對象的具有一個 String 類型的構造方法,// 再使用這個構造方法新建一個對象并將 p2 引用指向這個對象,// 即為通過反射的方式創建對象p2 = cp.getConstructor(String.class).newInstance("另一個人");} catch (Exception e) {e.printStackTrace();}if (p2 != null) {p2.work();}}public static void main(String[] args) {startTest();} }

在這里我們獲取了 p1 的類型信息之后利用反射新建了一個 People 對象,并調用了它的 work() 方法,我們來看看結果:

Ok,其實得到了一個對象的 Class 類型對象之后我們能做的事情有很多(新建對象,調用方法,甚至訪問類私有屬性/方法…)。當然這些都是 Java 反射的內容,有興趣的小伙伴可以查找相關資料。

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 {@code equals(Object)}* 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 java.lang.Object#equals(java.lang.Object)}* 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>* <p>* As much as is reasonably practical, the hashCode method defined by* class {@code Object} does return distinct integers for distinct* objects. (This is typically implemented by converting the internal* address of the object into an integer, but this implementation* technique is not required by the* Java&trade; programming language.)** @return a hash code value for this object.* @see java.lang.Object#equals(java.lang.Object)* @see java.lang.System#identityHashCode*/ public native int hashCode();

同樣的,這個方法默認是利用 C/C++ 語言實現的,這個方法可以返回一個對象的哈希值,這個值在一定程度上可以標志一個對象。默認情況下,這個值會和當前對象所在內存中的地址有一定的關系(這取決于 JVM 的實現),當然,我們在子類中可以根據子類的特性選擇重寫這個方法。而提到 hashCode 方法就不得不提 Java 中的 Map 接口下的相關容器了,因為 Map 接口下的一些容器(HashMap,IdentityHashMap)正是通過對象的 hashCode 方法進行工作的,在 HashMap 中,會有一個名為 table 的數組字段,這個數組字段用來儲存 HashMap 中每一個鍵值對關系,即為映射表,每當儲存一個新的鍵值對進入當前的 HashMap 對象的時候,都會調用這個鍵值對中的 “鍵” 對象的 hashCode 方法并將其返回的哈希值進行一定的處理,然后將這個結果作為數組的下標并且將要儲存的鍵值對儲存在 table 數組的這個下標元素中。當然這種做法可能會產生沖突,即多個鍵值對儲存時得到的下標值相同,關于 HashMap 處理沖突細節,可以參考一下我的下一篇文章。
除此之外,我們還在利用 hashCode 方法時需要注意一些問題:

1、如果兩個對象的 hashCode 返回值相同,我們不能直接說它們相等,原因很簡單:這個方法本身可以被子類重寫,我只需要定義一個類 A 并且重寫這個方法然后讓它返回一個固定值就行了,這樣的話所有 A 類的對象的 hashCode 方法返回值都相同,但實際上它們并不是同一個對象。
2、但是反過來:如果兩個對象的 hashCode 值不相等,那么我們就可以判斷這兩個對象一定不同(不是同一個對象)。這個道理很容易明白:如果兩個引用指向同一個對象,那么這兩個引用調用的 hashCode 方法一定是同一個對象的 hashCode 方法,同一個對象在同一時刻的 hashCode 方法返回值肯定相同,那么如果兩個對象的 hashCode 值不同,那么我們也可以判斷這兩個對象不等(內存上不是同一個對象)。

那么在我們自定義的類中我們如何去重寫這個方法呢,在《Java 編程思想》中提供了一個關于如何在自定義類中編寫一個合理的 hashCode 方法的參考:

JDK 里面已經提供了工具方法來幫助我們計算一個復雜類對象的 hashCode 值,我們來看看這個方法(java.util.Objects#hash):

/*** Generates a hash code for a sequence of input values. The hash* code is generated as if all the input values were placed into an* array, and that array were hashed by calling {@link* Arrays#hashCode(Object[])}.** <p>This method is useful for implementing {@link* Object#hashCode()} on objects containing multiple fields. For* example, if an object that has three fields, {@code x}, {@code* y}, and {@code z}, one could write:** <blockquote><pre>* &#064;Override public int hashCode() {* return Objects.hash(x, y, z);* }* </pre></blockquote>** <b>Warning: When a single object reference is supplied, the returned* value does not equal the hash code of that object reference.</b> This* value can be computed by calling {@link #hashCode(Object)}.** @param values the values to be hashed* @return a hash value of the sequence of input values* @see Arrays#hashCode(Object[])* @see List#hashCode*/public static int hash(Object... values) {return Arrays.hashCode(values);}

調用了 Arrays.hashCode 方法,我們繼續跟進 java.util.Arrays#hashCode(java.lang.Object[]) :

/*** Returns a hash code based on the contents of the specified array. If* the array contains other arrays as elements, the hash code is based on* their identities rather than their contents. It is therefore* acceptable to invoke this method on an array that contains itself as an* element, either directly or indirectly through one or more levels of* arrays.** <p>For any two arrays <tt>a</tt> and <tt>b</tt> such that* <tt>Arrays.equals(a, b)</tt>, it is also the case that* <tt>Arrays.hashCode(a) == Arrays.hashCode(b)</tt>.** <p>The value returned by this method is equal to the value that would* be returned by <tt>Arrays.asList(a).hashCode()</tt>, unless <tt>a</tt>* is <tt>null</tt>, in which case <tt>0</tt> is returned.** @param a the array whose content-based hash code to compute* @return a content-based hash code for <tt>a</tt>* @see #deepHashCode(Object[])* @since 1.5*/public static int hashCode(Object a[]) {if (a == null)return 0;int result = 1;for (Object element : a)result = 31 * result + (element == null ? 0 : element.hashCode());return result;}

JDK 采用的方案是 (((((res * 31) + element1.hashCode()) * 31 + element2.hashCode()) * 31 + element3.hashCode()) * 31 + ....); 的方案。

當然,這個也僅供我們參考,想要保證決定的 hashCode 不沖突是不可能的,因為 hashCode 的返回值是 int 類型,那么最多有 2^32 個值,如果內存中的對象超過了 2^32 個 。那么必定會有兩個或者兩個以上對象的 hashCode 值是相同的。

equals

這個方法本意是用來判斷兩個對象在 “值” 上是否等價的,但是在 Object 類中的默認實現卻是判斷兩個引用是否指向同一個對象,即比較對象的地址。我們來看看這個方法的源碼:

/*** 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>* 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}).* <p>* Note that it is generally necessary to override the {@code 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); }

如果我們在自定義的類中需要使用到這個方法,或者是我們使用的某些類中需要使用到相關類的 equals 方法(例如將自定義的類型作為 HashMap 對象的 “鍵”),我們就應該重寫這個方法,一般情況下,我們只需要對兩個對象中的每一個字段進行比較(如果是字段是引用的話再次調用該字段的 equals 方法)就可以了,例:

public class Main {// 車輪類static class Wheel {int radius;public Wheel(int radius) {this.radius = radius;}@Overridepublic boolean equals(Object obj) {return obj instanceof Wheel && radius == ((Wheel) obj).radius;}}static class Bike {// 車的前后輪Wheel frontWheel;Wheel backWheel;int weight;public Bike(Wheel frontWheel, Wheel backWheel, int weight) {this.frontWheel = frontWheel;this.backWheel = backWheel;this.weight = weight;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof Bike)) {return false;}Bike bike = (Bike) obj;return frontWheel.equals(bike.frontWheel) &&backWheel.equals(bike.backWheel) &&weight == bike.weight;}}private static void startTest2() {Bike bike1 = new Bike(new Wheel(1), new Wheel(2), 10);Bike bike2 = new Bike(new Wheel(1), new Wheel(2), 10);Bike bike3 = new Bike(new Wheel(2), new Wheel(2), 10);Bike bike4 = new Bike(new Wheel(2), new Wheel(2), 15);System.out.println("bike1 equals bike2: " + bike1.equals(bike2));System.out.println("bike1 equals bike3: " + bike1.equals(bike3));System.out.println("bike3 equals bike4: " + bike3.equals(bike4));}public static void main(String[] args) {startTest2();} }

來看看結果:

在《Java 編程思想》中關于如何重寫一個自定義類的 equals 方法也給出了幾條建議供我們參考

其實這幾條建議在上面方法的注釋說明中已經體現出來了。值得一提的是,在 hashCode 方法的注釋說明中,有提到關于 hashCode 方法和 equals 方法的關系:
如果一個對象和另一個對象通過 equals 方法判斷等價時返回 true ,那么他們的 hashCode 方法應該返回相同的整型值,相反,雖然我們并不要求兩個通過 equals 方法判斷不相等的對象的 hashCode 方法一定要返回不同的整型值,但是我們要知道將它們的 hashCode 設計成返回不同的整型值在某些場景下會有較好的性能表現(在 HashMap 作為 “鍵” 的儲存中可以減少沖突的次數)。簡單點來說就是假設現在有兩個同類型的對象 a,b,如果 a.equals(b) == false ,那么我們最好使得 a.hashCode() != b.hashCode() 成立。

clone

這個方法用于獲取一個當前對象的克隆對象(即對象的復制品)。我們來看一下它的源碼:

/*** 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.* <p>* 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*/ protected native Object clone() throws CloneNotSupportedException;

值得注意的是如果一個自定義的對象要支持 clone() 方法,那么他必須實現 Cloneable 接口(雖然這個接口沒有任何方法),但是實現這個接口的目的只是為了使得讓這個類的對象同時也是 Cloneable 類型的對象。來看個例子:

public class Main {static class Clone1 {int number;public Clone1(int number) {this.number = number;}public static void startTest3() {Clone1 c1 = new Clone1(1);try {Clone1 c1Copy = (Clone1) c1.clone();System.out.println(c1Copy.number);} catch (CloneNotSupportedException e) {e.printStackTrace();}}}public static void main(String[] args) {Clone1.startTest3();} }

結果:

正如 clone 方法的注釋中所說的,對沒有實現 Cloneable 接口的類調用 clone 方法是會拋出 CloneNotSupportedException 異常的。那么讓我們修改一下,只需修改一行代碼:

static class Clone1 implements Cloneable {

我們讓 Clone1 類實現了 Cloneable 接口,這個接口的源碼如下:

public interface Cloneable { }

為了突出重點,這里我沒有貼這個接口的注釋說明,重點是什么呢?它是一個空接口。所以我們在實現這個接口的時候無需重寫任何方法。Ok,再來運行一下:

這次就成功了!

深拷貝和淺拷貝

對于這個詞,我想學過 C++ 的小伙伴肯定非常熟悉,因為 C++ 中有拷貝構造函數的概念。當然,這里是 Java ,我們來看一下深拷貝、淺拷貝和 clone 方法的關系:
淺拷貝:顧名思義,它是一個淺顯的復制,我們可以理解成只復制值;
深拷貝:相對于淺拷貝來說,它是一個深入的復制,我們可以理解為它會復制整個對象(建立一個新的對象,新對象中的每一個屬性值和舊對象中的每一個屬性值都對應相同)。
我們來看一個例子:

Object o1 = new Object(); Object o1Copy = o1;

這是一個典型的淺拷貝的例子,我們只是簡單的進行了引用的復制,實現上兩個引用還是指向的同一個對象,再看一個深拷貝的例子:

class X {int num;public X(int num) {this.num = num;}public int getNum() {return num;} }X x1 = new X(2); X x1Copy = new X(x1.getNum());

很明顯,x1Copy 和 x1 兩個引用指向的是不同的 X 類型的對象,而同時兩個對象中的 num 屬性值又相同,這就是深拷貝,不僅僅拷貝字段的值,而且拷貝整個對象。用深拷貝生成的對象和原對象相互獨立,互不影響,而這個也是我們推薦的做法,因為這更加符合 “克隆” 的含義,試想,如果一個修改一個克隆的對象還會對原對象產生影響,那怎么能叫克隆呢?

那么對于一個自定義的類我們如果編寫具有深拷貝作用的 clone 方法呢?這里有 2 點參考:
1、對于類中的基本數據類型,直接復制;
2、對于類中的引用數據類型,引用賦值為被拷貝字段對象的 clone 方法的返回值。
看個例子:

public class Main {// 車輪類static class Wheel implements Cloneable {int radius;public Wheel(int radius) {this.radius = radius;}@Overridepublic boolean equals(Object obj) {return obj instanceof Wheel && radius == ((Wheel) obj).radius;}@Overrideprotected Object clone() throws CloneNotSupportedException {Wheel wheelCopy = (Wheel)super.clone();wheelCopy.radius = radius;return wheelCopy;}}static class Bike implements Cloneable {// 車的前后輪Wheel frontWheel;Wheel backWheel;int weight;public Bike(Wheel frontWheel, Wheel backWheel, int weight) {this.frontWheel = frontWheel;this.backWheel = backWheel;this.weight = weight;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof Bike)) {return false;}Bike bike = (Bike) obj;return frontWheel.equals(bike.frontWheel) &&backWheel.equals(bike.backWheel) &&weight == bike.weight;}@Overrideprotected Object clone() throws CloneNotSupportedException {// 先通過 super.clone() 方法獲取對應的對象Bike bikeCopy = (Bike)super.clone();// 基本數據類型直接賦值bikeCopy.weight = weight;// 引用數據類型通過調用被拷貝屬性對象的 clone 方法賦值bikeCopy.frontWheel = (Wheel) frontWheel.clone();bikeCopy.backWheel = (Wheel) backWheel.clone();return bikeCopy;}}private static void startTest() {Bike bike1 = new Bike(new Wheel(1), new Wheel(2), 5);try {Bike bike2 = (Bike) bike1.clone();System.out.println("bike1 == bike2: " + (bike1 == bike2));System.out.println("bike1.equals(bike2): " + (bike1.equals(bike2)));System.out.println("bike1.frontWheel == bike2.frontWheel: " + (bike1.frontWheel == bike2.frontWheel));System.out.println("bike1.frontWheel.equals(bike2.frontWheel): " + (bike1.frontWheel.equals(bike2.frontWheel)));} catch (CloneNotSupportedException e) {e.printStackTrace();}}public static void main(String[] args) {startTest();} }

還是使用上面出現過的 Wheel 類和 Bike 類,只不過對它們做了一些改動。我們來看看結果:

可以看到,直接使用 == 運算符判斷兩個 Bike 對象返回 false,證明在內存上不是同一個對象,而調用 equals 方法得到的結果卻是 true,證明兩個 Bike 對象在屬性值上是等價的。對于它們的組件 frontWheel 字段也是如此。這樣,我們就完成了對復雜對象的深拷貝。

toString

這個方法我想小伙伴們都不會陌生,這是我們最常用的對象方法之一了,其作用也很簡單,就是返回對象的字符串內容表示,我們來看看這個方法的源碼聲明:

/*** Returns a string representation of the object. 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.* <p>* 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()); }

Object 類中這個方法的默認實現是 對象的類名@對象hashCode的16進制表示。當然,我們可以根據自己的需求重寫。

wait / notify / notifyAll

先看一下方法聲明:

/*** Causes the current thread to wait until either another thread invokes the* {@link java.lang.Object#notify()} method or the* {@link java.lang.Object#notifyAll()} method for this object, or a* specified amount of time has elapsed.* ...*/ public final native void wait(long timeout) throws InterruptedException;/*** Causes the current thread to wait until another thread invokes the* {@link java.lang.Object#notify()} method or the* {@link java.lang.Object#notifyAll()} method for this object.* In other words, this method behaves exactly as if it simply* performs the call {@code wait(0)}.* ...*/ public final void wait() throws InterruptedException {wait(0); }/*** Causes the current thread to wait until another thread invokes the* {@link java.lang.Object#notify()} method or the* {@link java.lang.Object#notifyAll()} method for this object, or* some other thread interrupts the current thread, or a certain* amount of real time has elapsed.* ...*/ public final void wait(long timeout, int nanos) throws InterruptedException {if (timeout < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos > 0) {timeout++;}wait(timeout); }/*** Wakes up a single thread that is waiting on this object's* monitor. If any threads are waiting on this object, one of them* is chosen to be awakened. The choice is arbitrary and occurs at* the discretion of the implementation. A thread waits on an object's* monitor by calling one of the {@code wait} methods.* ...*/ public final native void notify();/*** Wakes up all threads that are waiting on this object's monitor. A* thread waits on an object's monitor by calling one of the* {@code wait} methods.* ...*/ public final native void notifyAll();

這幾個方法其實都是和多線程有關,并且主要用于控制多個線程的同步問題,其實了解 Java 多線程機制的小伙伴對這幾個方法應該不會陌生,這里還是簡單介紹一下這三個方法:
1、wait:這個方法有三個重載的版本(其實終歸調用的都是同一個方法),主要作用是使得當前線程讓出所持有的對象鎖并陷入阻塞狀態,另外兩個帶有參數的版本可以控制線程的阻塞時間,阻塞時間達到了參數所指定的時間時就喚醒該線程。當然也可以在線程阻塞過程中通過這個對象的 notify / notifyAll 方法喚醒;
2、notify:隨機喚醒一個因為調用了當前對象的 wait 方法而陷入阻塞的線程;
3、notifyAll:喚醒全部因為調用了當前對象的 wait 方法而陷入阻塞的線程。

這里大概介紹了一下這三個方法的作用,有興趣深入了解的小伙伴可以參考一下我的多線程系列文章。當然也可以直接看 這篇文章 中對這幾個方法的解釋。

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.* ....*/ protected void finalize() throws Throwable { }

這個方法是在 JVM 進行垃圾回收的時候可能會調用(注意我這里寫的是可能)的方法。在 JVM 進行垃圾回收時,首先得獲取到哪些對象是可以進行垃圾回收的,對于如何獲取到可以回收的對象,有兩種被提出的算法:
引用計數和可達性分析,簡單看一下這兩種方法:
1、引用計數:顧名思義,對每個對象設置一個引用計數器,記錄當前對象被多少個引用所持有,如果某個對象的引用數為 0 ,那么證明該對象可以被回收;
這種算法咋一看挺不錯的,實現簡單,其實有很多問題,比如說不能解決對象之間互相引用的問題:假設有兩個對象 A、B,A 中有一個 Object 類型的引用指向對象 B,B 中也有一個 Object 類型的引用指向對象 A,那么按照這種算法這兩個對象永遠不會被回收,但其實這兩個對象都沒有被外界用到。
2、可達性分析:這種算法基于深度優先搜索的思想,從 root 對象出發,找出 root 對象中的所有引用字段,再循環找出 root 對象中所有引用字段指向的對象中的引用字段,… 來看張圖:

在這里面 B C D E F G H 對象都是不可回收的,因為他們都直接 / 間接的被 root 用到了。現在的關鍵問題是如何選擇合適的 “root” 了。一般來說,JVM 選擇的 root 都會是聲明周期較長的,所以一般 JVM 會將類中的一些靜態字段、常量字段和靜態方法中的一些對象作為 “root” 。這種方法也是主流的虛擬機在進行垃圾回收時采用的尋找可以被回收的對象的算法。

簡單介紹了一下 JVM 中的垃圾回收中如何找到需要被回收的對象,我們再來看看這個和 finalize 方法的關系,在 JVM 找到了當前要回收的對象之后(或者說標記了要回收的對象之后),**會開一個優先級較低的線程來執行這些對象的 finalize 方法(也有可能不開,這取決于 JVM 的狀態)。**需要注意的是,即使開了線程去執行這些對象的 finalize 方法,由于線程優先級較低,所以可能在執行過程中由于系統資源不足就被回收了。所以在一開始我便標注了是可能被執行。另外,某個對象的 finalize 方法只會被 JVM 調用一次,即調用過之后這個對象的 finalize 方法就不會再被調用了。
我們還是舉個例子來理解這個過程,先上代碼:

public class Main {static Test obj;static class Test {@Overrideprotected void finalize() throws Throwable {super.finalize();Main.obj = this;}}public static void main(String[] args) throws InterruptedException {Test t = new Test();t = null;// 啟動 JVM 的垃圾回收動作System.gc();// 因為對象的 finalize 方法在一個優先級很低的線程中運行,// 所以主線程休眠 1 秒確保對象的 finalize 方法執行完成Thread.sleep(1000);if (Main.obj == null) {System.out.println("t 對象被回收了");} else {System.out.println("t 對象沒有被回收");}} }

我們在創建了一個新的 Test 對象之后將 t 設置為 null,之后手動調用垃圾回收動作,如果對象的 finalize 方法被調用了,那么就會有一個 static (靜態)類型的 Test 引用指向這個對象,那么這個對象就不會被回收,否則的話這個對象就被回收了,我們來看看程序的結果:

結果中 t 對象并沒有被回收,證明對象的 finalize 方法確實被執行了。那么如何反推它會被回收的情況呢?我們只需要修改一下 main 方法中的代碼就行了:

public static void main(String[] args) throws InterruptedException {Test t = new Test();t = null;// 啟動 JVM 的垃圾回收動作System.gc();// 因為對象的 finalize 方法在一個優先級很低的線程中運行,// 所以主線程休眠 1 秒確保對象的 finalize 方法執行完成Thread.sleep(1000);if (Main.obj == null) {System.out.println("t 對象被回收了");} else {System.out.println("t 對象沒有被回收");}// 現將 obj 引用置為 null,排除干擾因素obj = null;// 啟動 JVM 的垃圾回收動作System.gc();// 因為對象的 finalize 方法在一個優先級很低的線程中運行,// 所以主線程休眠 1 秒確保對象的 finalize 方法執行完成Thread.sleep(1000);if (Main.obj == null) {System.out.println("t 對象被回收了");} else {System.out.println("t 對象沒有被回收");} }

就是在原來的 main 方法后面加了一端幾乎一樣的代碼(在前面多了一句 obj = null;)。我們再來看看結果:

可以看到,因為 finalize 方法的因素,第一次 t 對象沒有被回收,而第二次 t 對象已經被回收了,證明 finalize 方法只被調用了一次,也證實了我們上面的結論。

由此我們也知道了:我們不應該將對象的相關資源回收的代碼放在 finalize 方法中執行,因為 JVM 不保證這個方法每一次都會得到執行,也正因為如此,這個方法在平常開發并不常用,對于對象的資源回收,我們可以專門寫一個方法處理都會得到比直接使用這個方法更好的表現。

好了。Java Object 類方法解析就到這里了,相信你對 Java Object 類中的方法啊有一個更深入的理解。如果博客中有什么不正確的地方,還請多多指點。如果這篇文章對您有幫助,請不要吝嗇您的贊,歡迎繼續關注我的其他文章。

謝謝觀看。。。

我的博客即將搬運同步至騰訊云+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=3o2apkp87k2sc

總結

以上是生活随笔為你收集整理的Java Object 类方法解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。