本文閱讀時間大約5分鐘。
如果將物件作為Map中的key,需要是實現該物件的equals方法和hashCode方法;現在一般透過lombok可以簡單得實現,並且可以選擇具體需要哪些欄位參與equals和hashCode方法的計算。
Java型別系統中分為基礎型別和取用型別,取用型別中所有的物件都有一個父類——java.lang.Object。基類Object提供了一些可擴充套件的方法:equals、hashCode、toString、clone和finalize。開發者在改寫這些方法的時候,要遵循一定的約定,如果使用不當就會造成bug。
如果類有自己的“邏輯相等”概念,而且父類的equals方法又無法滿足期望的時候,就應該改寫equals方法。在開發中我們有時候會將一個自定義的物件作為map中的key,或者將一個自定義的物件加入到集合中,這時候就需要改寫equals方法。
改寫equals方法的時候,要同時改寫hashCode方法。這裡一起看一個案例。假設我定義一個使用者資訊類,程式碼如下所示:
這裡使用@EqualsAndHashCode註解生成equals和hashCode方法,併排除了除userId以外的其他欄位,表示該使用者資訊物件的唯一性只跟userId這個欄位有關。如果該類是繼承了某個自定義的類,需要考慮父類的欄位,那麼還可以使用@EqualsAndHashCode中的callSuper欄位,設定為true就會連父類的欄位一起考慮,預設是隻考慮當前類中的欄位。關註lombok的用法,這裡不展開講了。
假設有一個場景,需要過濾確保某個串列裡的使用者物件是沒有重覆的,那麼我們就需要確定使用者物件的唯一id是什麼?在這裡是userId,可以使用集合來進行重覆物件的過濾,程式碼如下所示:
這個案例的執行結果也很明顯,輸出的值是:2。
所有自定義的類都要改寫toString方法,我會使用lombok的@ToString註解來幫我生成toString方法。使用toString方法可以將物件的欄位都以可讀的形式展示出來。這樣在列印日誌的時候,要列印某個物件,就不會打印出一個物件的地址,類似於UserInfo@1768b4,這種展示出來對排查問題一點幫助沒有。
我在開發中沒有用過這個方法。要完成物件的複製,只需要區分自己是要深複製還是淺複製。一般我會使用複製建構式或靜態工廠方法作為替代方案。
根據Java檔案,finalize方法被設計出來是用於釋放非Java資源,但是由於jvm的執行機制導致有很大可能不會呼叫到物件的finalize方法,或者呼叫的時機和順序是不確定的,所以這個方法並沒有達到其設計標的。Java9中這個方法已經被廢棄了,不過現在很多面試還是會問到這個方法背後的原理,需要理解幾個概念:
-
自定義類的物件,就是我們自定義的類,該類改寫了finalize方法;
-
Finalizer物件,在新建一個改寫了finalize方法的類的物件的時候,就會伴生一個Finalizer物件,並將該物件加入到一個雙向串列中;
-
ReferenceQueuequeue,Finalizer物件創建出來後,就會被加入到這個雙向串列;
-
FinalizerThread物件,Finalizer執行緒是3號執行緒,它的作用就是不斷從上面哪個佇列中取Finalizer物件,然後呼叫它的runFinalzier方法。
在Java應用中,如果對finalize方法使用不合理,有時候會引發一類問題——存放Finalizer的雙向串列過長,導致一些物件的finalze方法呼叫延遲,如果程式在這個方法中進行了某些對時間敏感的資源的釋放,那麼就會有問題。
-
《Effective Java中文版(2)》
-
https://zhuanlan.zhihu.com/p/29522201
-
https://www.jianshu.com/p/9d2788fffd5f
-
-
https://stackoverflow.com/questions/5175811/finalize-method-in-java
朋友會在“發現-看一看”看到你“在看”的內容