1. 程式人生 > >Java基礎學習之--理解Object類

Java基礎學習之--理解Object類

看Java API的Object類, 一共11個方法。按使用的頻度排名:

  1. toString() 這個方法最常用在打日誌,定位程式碼問題。

  2. equals()hashCode(), 這兩個方法的使用經典例子是HashMap的原始碼
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        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;
    }

equals()hashCode() 要常用一點。

  1. wait()notify() 這個開發很少會直接用到,但是間接用到的場景不少,屬於偏內功的點。wait/notify屬於Object類最難理解的點了,因為它的基石是多執行緒。學習思路還是三步走。

step 1: 看文件說明

wait()
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.

notify()
Wakes up a single thread that is waiting on this object's monitor.

這個文件說明,看完基本一頭霧水,兩個方法都提到了如下的核心概念thread, object's monitor。 先把這些概念放一邊,看看是怎麼用的。

step 2: 執行Demo

給個demo輔助理解,一個執行緒是幹活的,另一個執行緒是管事的。管事的等活幹完驗收。

public class WaitNotifyDemo {

    static class Worker extends Thread{

        private volatile int status=0; // 1: 完成, -1: 出錯

        @Override
        public void run(){

            try {
                System.out.println(Thread.currentThread().getName() +": 開始幹活");
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() +": 完成任務");

                status = 1;
                // 通知主執行緒
                synchronized (this){
                    this.notify();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
                status = -1;
            }
        }

        public int getStatus(){
            return status;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Worker worker= new Worker();
        worker.start();

        synchronized (worker){
            int status;
            while ((status=worker.getStatus())!=1){
                worker.wait();
                if(status==-1)
                    throw new RuntimeException("出錯了");
            }
        }
        System.out.println("任務已經完成");
    }
}

step3: 折騰demo
接下來, 我試了一下, 把notify的程式碼去掉,也能正常執行。 這就讓人困惑了,文件明明說必須呼叫notify,wait才能結束。接下來再看文件:

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup

所以, wait方法必須在while迴圈中呼叫。好,解答了一點疑惑。但是每次Worker執行緒結束時沒有呼叫notify,主執行緒就能正常退出, 這個也說不通。 唯一的解釋是: JVM內部在某個地方呼叫了notify。看openjdk的原始碼, 果然如此:從start0開始, 定位到執行緒在退出時會呼叫lock.notify_all(thread);。只是這裡的程式碼是JVM內部的程式碼,比較底層。

其實,這個例子更簡潔的寫法是worker.join()。 如果看Thread.join()的原始碼,發現它的實現恰好就是呼叫了wait。

  1. clone() 這個需要理解深度克隆, 知識點不復雜。

  2. getclass() 這個放在後面,但是用得還挺多的,特別是寫框架。

  3. finalize() 這個已經不他推薦使用了, 儘量不干擾GC的節奏。

總結一下,Object類是Java的基石。這裡比較難一點的就是wait/notify. 學習Java的API, 如果想理解透徹一點,估計繞不開JVM的c++原始碼。