Java Object對象

Object.getClass()和Object.class區別

  • 類名.class叫做“類字面量”,因class是關鍵字, 所以類名.class編譯時確定。而getclass()是某個具體的方法來調用,是運行時根據實際實例確定,getClass()是動態而且是final的

HashCode

  • ava中的hashCode方法就是根據一定的規則將與對象相關的信息(比如對象的存儲地址,對象的字段等)映射成一個數值,這個數值稱作爲散列值
  • Hashcode在集合中添加新的元素提高很多效率。如果集合中已經存在一萬條數據或者更多的數據,如果採用equals方法去逐一比較,效率必然是一個問題。此時hashCode方法的作用就體現出來了,當集合要添加新的對象時,先調用這個對象的hashCode方法,得到對應的hashcode值,實際上在HashMap的具體實現中會用一個table保存已經存進去的對象的hashcode值,如果table中沒有該hashcode值,它就可以直接存進去,不用再進行任何比較了;如果存在該hashcode值, 就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址
  • HashMap 添加元素實現
public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

可以直接根據hashcode值判斷兩個對象是否相等嗎?

肯定是不可以的,因爲不同的對象可能會生成相同的hashcode值。如果兩個對象的hashcode值不等,則必定是兩個不同的對象,hashcode相等,兩個對象不一定相等,如果要判斷兩個對象是否真正相等,必須通過equals方法。

  • 兩個對象,如果調用equals方法得到的結果爲true,則兩個對象的hashcode值必定相等
  • 如果equals方法得到的結果爲false,則兩個對象的hashcode值不一定不同
  • 如果兩個對象的hashcode值不等,則equals方法得到的結果必定爲false
  • 如果兩個對象的hashcode值相等,則equals方法得到的結果未知

Clone方法

protected native Object clone() throws CloneNotSupportedException;
  • 對於任何對象 x,表達式 x.clone() != x 爲true,x.clone().getClass() == x.getClass() 也爲true
  • Cloneable和Serializable一樣都是標記型接口,它們內部都沒有方法和屬性,implements Cloneable表示該對象能被克隆,能使用Object.clone()方法.由於Object本身沒有實現Cloneable接口,所以不重寫clone方法並且進行調用的話會發生CloneNotSupportedException異常
  • Clone 要注意深拷貝還是淺拷貝
    (1)淺克隆(shallow clone),淺拷貝是指拷貝對象時僅僅拷貝對象本身和對象中的基本變量,而不拷貝對象包含的引用指向的對象。
    (2)深克隆(deep clone),深拷貝不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。

wait/notify/notifyAll

Java可以使用wait/notify/notifyAll實現線程間的通信,在瞭解wait 和 notify 之前,我們先了解下Java對象的監視器(monitor

Monitor–對象監視器

  • JVM 給每個對象和Class字節碼都設置了一個監視器Monitor,用於檢測併發代碼的重入,同時還在 Object類中提供了notify和wait方法來對線程進行控制
  • 在這裏插入圖片描述
  1. Monitor可以類比一個房間,他保證每次只有一個線程能進入到這個房間裏訪問被保護的數據
  2. 當線程需要訪問被保護的數據時,排隊在一個叫Entry Set的隊列中,等待佔用這個Monitor的線程釋放Monitor後去競爭獲取這個監視器
  3. 當線程釋放了Monitor後就進入了Wait Set隊列,當對象調用了notify之後,Wait Set隊列中的線程就會被再次喚醒,只不過也許要競爭,只有一個線程才能獲得監視器
  4. 總之Monitor是對於Object而言的,任何情況下,都只有一個線程持有該對象的監視器

Monitor實現機制

有如下代碼

public class Test {
    public static void main(String[] args) {
        Object object = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

在另一個線程中去調用wait方法,會拋出如下異常

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Unknown Source)
	at Test$1.run(Test.java:9)
	at java.lang.Thread.run(Unknown Source)

這是因爲我們在主線程中創建了obj對象,在子線程中調用了wait方法,但是obj實際控制權還在主線程中

如何獲取對象的監視器

在Java中,我們使用synchronized實現線程同步來獲取對象的監視器

synchronized(Obejct obj) {
    //同步代碼塊
    ...
}
public synchronized void set(int val) {
    this.val = val;
}
public static synchronized void set(Test instance) {
    Test.instance = instance;
}
  1. synchronized修飾的obj是類.class時,標識獲取該類字節碼的監視器,但是對於訪問對象的實例方法並不受影響
  2. synchronized修飾的是非靜態方法時,實際上獲取的是this對象的監視器,其他需要獲取該對象打的監視器會受到阻塞
  3. synchronized修飾的是靜態方法時,因爲static方法不屬於任何對象,而是屬於類的方法,獲取的是類字節碼的監視器,同1

線程間通信方法

線程間的通信可以使用wait、notify、notifyAll來進行控制,wait()來讓一個線程在某些條件下暫停運行,notify去通知一個線程運行,而notifyAll會喚醒所有等待中的線程。

notify和notifyAll

  • 前者只能喚醒一個正在等待這個對象的monitor的線程,具體由JVM決定,後者則會喚醒所有正在等待這個對象的monitor的線程,但是要注意:
  1. 調用notify方法,並不意味着釋放了Monitor,必須要等同步代碼塊結束後纔會釋放Monitor
  2. 多線程之間如果wait先於notify執行,或者是notify時沒有wait的線程,那麼notify不會有任何喚醒效果,或導致其他線程

wait

  1. wait()方法執行時,會釋放鎖,同時線程掛起
  2. 永遠在循環(loop)裏調用 wait 和 notify,不是在 If 語句
  3. wait()/notify()/notifyAll() 方法在使用時, 必須使用同一個對象的鎖。

finalize方法

類似於C++的析構函數,當類對象的內存不再使用時,GC會調用這個方法,該方法中不要激活對象的引用,不然會造成假死狀態,導致GC不會立馬回收該對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章