Java中Object類的理解和使用

程序猿凱撒 發佈 2023-11-25T05:13:15.940732+00:00

如何理解根父類類java.lang.Object是類層次結構的根類,即所有其它類的父類。每個類都使用Object作為超類。

如何理解根父類

類java.lang.Object是類層次結構的根類,即所有其它類的父類。每個類都使用Object作為超類。

  • Object類型的變量與除Object以外的任意引用數據類型的對象都存在多態引用
java複製代碼method(Object obj){…} //可以接收任何類作為其參數
Person o = new Person();  
method(o);
  • 所有對象(包括數組)都實現這個類的方法
  • 一個類沒有特別指定父類,那麼默認則繼承自Object類
java複製代碼public class Person {
        ...
}
//上面和下面是等價的
public class Person extends Object {
        ...
}

Object類的方法

根據JDK原始碼及Object類的API文檔,Object類中包含的方法有11個,但是今天只看其中主要的5個。

equals()

所有類都繼承了Object,那麼也就獲得了equals()方法,且還可以重寫方法。 equals():

  • 只能比較引用類型,比較是否指向同一個對象
  • 格式:obj1.equals(obj2)
  • 特例:當用equals()方法進行比較時,對類File、String、Date及包裝類來說,是比較類型及內容而不考慮引用的是否是同一個對象,因為在這些類中重寫了Object類的equals()方法
  • 當自定義equals()方法時,可以重寫方法,用來比較兩個對象的內容是否一樣

重寫equals()方法的原則:

  • 對稱性:如果x.equals(y)返回是true,那麼y.equals(x)也應該返回是true
  • 自反性:x.equals(x)必須返回是true
  • 傳遞性:如果x.equals(y)返回是true,且y.equals(z)返回是true,那麼z.equals(x)也應該返回true
  • 一致性:如果x.equals(y)返回是true,只要x和y內容不變,無論重複x.equals(y)多少次,都是返回true
  • 任何情況下,x.equals(null)都是返回false,x.equals(和x是不同類型的對象)都是返回false

重寫舉例:

java複製代碼class User{
        private String host;
        private String username;
        private String password;
        public User(String host, String username, String password) {
                super();
                this.host = host;
                this.username = username;
                this.password = password;
        }
        public User() {
                super();
        }
        public String getHost() {
                return host;
        }
        public void setHost(String host) {
                this.host = host;
        }
        public String getUsername() {
                return username;
        }
        public void setUsername(String username) {
                this.username = username;
        }
        public String getPassword() {
                return password;
        }
        public void setPassword(String password) {
                this.password = password;
        }
        @Override
        public String toString() {
                return "User [host=" + host + ", username=" + username + ", password=" + password + "]";
        }
        @Override
        public boolean equals(Object obj) {
                if (this == obj)
                        return true;
                if (obj == null)
                        return false;
                if (getClass() != obj.getClass())
                        return false;
                User other = (User) obj;
                if (host == null) {
                        if (other.host != null)
                                return false;
                } else if (!host.equals(other.host))
                        return false;
                if (password == null) {
                        if (other.password != null)
                                return false;
                } else if (!password.equals(other.password))
                        return false;
                if (username == null) {
                        if (other.username != null)
                                return false;
                } else if (!username.equals(other.username))
                        return false;
                return true;
        }
}

= =:

  • 基本類型比較的是值:只要兩個變量的值相等,即為true
  • 引用類型比較引用(是否指向同一個對象):只有指向同一個對象時,才返回true

注意: 用來進行比較時,符號兩邊的數據類型必須兼容(可自動轉換的基本數據類型除外),否則編譯報錯。

= =和equals的區別:

  • = =即可以比較基本數據類型也可以比較引用類型,對於基本類型就是比較數值,對於引用類型就是比較內存地址
  • equals是屬於java.lang.Object類裡面的方法,如果該方法沒有被重寫,默認也是= =,String等類的equals方法是被重寫過的,而且String類在日常開發中用的較多,形成了equals是比較值的錯誤觀點,這點要注意
  • 具體要看自定義類型里有沒有重寫Object的equals方法來判斷
  • 通常情況下,重寫equals方法,會比較類中的相應屬性是否都相等

toString()

public String toString():默認情況下toString()返回的是對象的運行時類型@對象的hashCode值的十六進位形式。

在進行String與其他類型數據的連接操作時,自動調用toString方法,比如:

java複製代碼Date now=new Date();
System.out.println("now="+now); //相當於"now="+now.toString()

如果直接列印對象,默認會調用該對象的toString()方法(java的引用數據類型的變量中存儲的實際上是對象的內存地址,但是Java對外隱藏了內存地址信息,所以不能直接將內存地址顯示出來,所以當列印對象時,JVM會調用對象的toString()方法)。

可以根據需要在用戶自定義類型中重寫toString()方法。

getClass()

public final Class<?> getClass():獲取對象的運行時類型。

由於Java有多態現象,所以一個引用數據類型的變量編譯時類型與運行時類型可能不一致,因此如果需要查看這個變量實際指向的對象的類型,就需要用getClass()方法。

java複製代碼public static void main(String[] args) {
        Object obj = new Person();
        System.out.println(obj.getClass()); //獲取運行時類型
}

hashCode()

public int hashCode():返回每個對象的hash值。

如果重寫equals,那麼通常會一起重寫hashCode()方法,hashCode()方法主要是為了當對象存儲到哈希表等容器中時提高存儲和查詢性能用的,這是因為關於hashCode()有兩個常規協定:

  • 如果兩個對象的hash值不同,那麼這兩個對象一定不相等
  • 如果兩個對象的hash值是相同的,那麼這兩個對象不一定相等

重寫equals()和hashCode()方法時,要保證滿足如下要求:

  • 如果兩個對象調用equals返回true,那麼要求這兩個對象的hashCode值一定是相等的
  • 如果兩個對象的hashCode值不同,那麼要求這兩個對象調用equals方法一定是false
  • 如果兩個對象的hashCode值相同,那麼這兩個對象調用equals可能是true,也可能是false
java複製代碼public static void main(String[] args) {
        System.out.println("Aa".hashCode()); //2112
        System.out.println("BB".hashCode()); //2112
}

clone()

clone()方法將對象複製了一份並返回給調用者,clone()的作用在於複製對象,在複製對象的過程中,首先要分配一個和源對象同樣大小的空間,在這個空間中創建一個新的對象。

示例:

java複製代碼public class CloneTest {
        public static void main(String[] args) {
                Animal a1 = new Animal("小黑");
                try {
                        Animal a2 = (Animal) a1.clone();
                        System.out.println("原始對象:" + a1);
                        a2.setName("小黃");
                        System.out.println("clone的對象:" + a2);
                } catch (CloneNotSupportedException e) {
                        e.printStackTrace();
                }
        }
}

class Animal implements Cloneable{
        private String name;
        public Animal() {
                super();
        }
        public Animal(String name) {
                super();
                this.name = name;
        }
        public String getName() {
                return name;
        }
        public void setName(String name) {
                this.name = name;
        }
        @Override
        public String toString() {
                return "Animal [name=" + name + "]";
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
                return super.clone();
        }
}

finalize()

當對象被回收時,系統自動調用該對象的finalize()方法,子類可以重寫該方法,做一些釋放資源的操作。如果重寫該方法,讓一個新的引用變量重新引用該對象,則會重新激活對象。

永遠不要主動調用某個對象的finalize方法,應該交給垃圾回收機制調用。

什麼時候被回收:

當某個對象沒有任何引用時,JVM就認為這個對象是垃圾對象,就會在之後不確定的時間使用垃圾回收機制來銷毀該對象,在銷毀該對象前,會先調用finalize()方法(垃圾回收發生具有不可預知性,程序無法精確控制垃圾回收機制的執行)。

垃圾回收機制的調用是由系統來決定的,也可以通過System.gc()或者Runtime.getRuntime().gc()來通知系統進行垃圾回收,會有一些效果,但是系統是否進行垃圾回收依然是不確定的。

java複製代碼public class FinalizeTest {
        public static void main(String[] args) {
                Person p = new Person("Peter", 12);
                System.out.println(p);
                p = null; //此時對象實體就是垃圾對象,等待被回收,但時間不確定
                System.gc();//強制性釋放空間
        }
}

class Person{
        private String name;
        private int age;
        public Person(String name, int age) {
                super();
                this.name = name;
                this.age = age;
        }
        public String getName() {
                return name;
        }
        public void setName(String name) {
                this.name = name;
        }
        public int getAge() {
                return age;
        }
        public void setAge(int age) {
                this.age = age;
        }
        //子類重寫此方法,可在釋放對象前進行某些操作
        @Override
        protected void finalize() throws Throwable {
                System.out.println("對象被釋放--->" + this);
        }
        @Override
        public String toString() {
                return "Person [name=" + name + ", age=" + age + "]";
        }
}

native關鍵字的理解

使用native關鍵字說明這個方法是原生函數,也就是這個方法是用c/c++等非Java語言實現的,並且被編譯成了dll,由Java去調用,在定義一個native方法時,並不提供實現體。

為什麼要用native方法

雖然Java使用起來非常方便,然而有些層次的任務用Java實現起來不易,或對程序的效率很在意時就會考慮native方法。

例如:有時Java應用需要與Java外面的環境交互,這是本地方法存在的主要原因,當Java需要與一些底層系統如作業系統或某些硬體交換信息時的情況,本地方法正是這樣的一種交流機制,它提供了一個非常簡潔的接口,而且無需我們去了解Java應用之外的繁瑣細節。

native聲明的方法,對於調用者可以當做其他Java方法一樣使用

一個native方法可以返回任何Java類型,包括非基本類型,而且同樣可以進行異常控制。

native方法的存在並不會對其他類調用這些本地方法產生任何的影響,實際上調用這些方法的其他類甚至不知道它調用的是一個本地方法,JVM將控制調用本地方法的所有細節。

如果一個含有本地方法的類被繼承,子類會繼承這個本地方法並且可以用Java在需要的時候重寫該方法。

關鍵字: