摘要:無論在中出現什么,都可以認為它是對象除了八大基本數據類型。讓當前線程等待某個對象的鎖,當然應該通過這個對象來操作了。但是要注意的是方法調用后,被喚醒的線程不會立馬獲得到鎖對象。主要的區別在于在釋放同時,釋放了對象鎖的控制。
前言
五一回家又斷更了一個放假時間了~~~
只有光頭才能變強
回顧前面:
ThreadLocal就是這么簡單
多線程三分鐘就可以入個門了!
多線程基礎必要知識點!看了學習多線程事半功倍
Java鎖機制了解一下
AQS簡簡單單過一遍
Lock鎖子類了解一下
之前花了點之間時間去搞多線程的基礎知識了,難呀難呀難呀....打算還寫一篇線程池的就暫時將多線程系列停止了...
今天中午在逛簡書的時候發現一些大廠也會問Object對象里面有什么方法(也算是一個知識點吧),Object我還沒去認真復習過,于是這篇主要看看Object對象有什么要注意的地方~
那么接下來就開始吧,如果文章有錯誤的地方請大家多多包涵,不吝在評論區指正哦~
一、Object對象簡介聲明:本文都是使用JDK1.8
我們學Java的知道,Java是一門面向對象的語言。無論在Java中出現什么,都可以認為它是對象(除了八大基本數據類型。當然了,八大基本數據類型也能裝箱成為對象):
而Object就是這些對象的最高級別的,所有的Java對象都隱式地繼承了Object對象(不用顯示寫extends繼承)
所有的Java對象都擁有Object默認的方法。
那么我們看看Object有什么方法:
其實就可以歸納成幾個:
registerNatives()【底層實現、不研究】
hashCode()
equals(Object obj)
clone()
toString()
notify()
notifyAll()
wait(long timeout)【還有重載了兩個】
finalize()
Object一共有11個方法,其中一個為底層的實現registerNatives(),其中兩個wait()和wait(long timeout, int nanos)重載方法。
所以我們真正需要看的就是8個方法
還有一個屬性:
public final native Class> getClass();二、equals和hashCode方法
equals和hashCode方法可以說是面試的重點題了,配合著String可以說在面試題中哪都有它們的存在。
首先,我們來看看equals和hashCode在Object中原生的實現吧:
hashCode:
public native int hashCode();
equals:
public boolean equals(Object obj) { return (this == obj); }
看上去都非常簡單:
hashCode()由native方法底層實現了。
equals()就直接==判斷是否相等了。
想要更加清晰它們究竟是做什么的,我們來讀讀它的注釋:
根據注釋我們可以總結以下的要點:
重寫equals()方法,就必須重寫hashCode()的方法
equals()方法默認是比較對象的地址,使用的是==等值運算符
hashCode()方法對底層是散列表的對象有提升性能的功能
同一個對象(如果該對象沒有被修改):那么重復調用hashCode()那么返回的int是相同的!
hashCode()方法默認是由對象的地址轉換而來的
equals()方法還有5個默認的原則:
自反性--->只要對象的不為null,x.equals(x)應該返回的是true
一致性--->只要對象沒有被修改,那么多次調用還是返回對應的結果!
傳遞性--->x.equals(y)和y.equals(z)都返回true,那么可以得出:x.equals(z)返回true
對稱性--->x.equals(y)和y.equals(x)結果應該是相等的。
傳入的參數為null,返回的是false
為啥說hashCode()以散列表為底層帶來性能的提升是很容易理解的。我們再來回顧一下HashMap的插入:
如果hash值都不相等,那么可以直接判斷該key是不相等的了!
2.1equals和hashCode方法重寫equals()方法默認是比較對象的地址,使用的是==等值運算符。但是按我們正常開發來說,比較的是對象地址是沒有意義的。
一般地,如果我們有兩個Address對象,只要這兩個對象的省號、城市號、街道號相等,我們就認為這兩個對象相等了!
2.2String實現的equals和hashCode方法我們在初學的時候可能就聽過了:String已經實現了equals和hashCode方法了。
這也就是為什么,我們可以直接使用String.equals()來判斷兩個字符串是否相等!
下面我們就來看看它的實現吧:
三、toString方法接下來我們看看toString方法,也十分簡單:
toString方法主要是用來標識該對象的:
從上面的結果我們都可以看出來:得出的結果我們并不能看到什么東西~
于是我們一般都重寫toString(),那么打印出的結果就很方便我們調試了!
@Override public String toString() { return "Address{" + "provinceNo=" + provinceNo + ", cityNo=" + cityNo + ", streetNo=" + streetNo + "}"; }
下面的結果看起來就好多了:
四、clone方法我們也來看看它的頂部注釋:
看了上面的注釋我們可以總結以下的要點:
clone方法用于對象的克隆,一般想要克隆出的對象是獨立的(與原有的對象是分開的)
深拷貝指的是該對象的成員變量(如果是可變引用)都應該克隆一份,淺拷貝指的是成員變量沒有被克隆一份
下面我們來看一下淺拷貝:拷貝了Employee對象,但是其成員變量hireday沒有被克隆出去,所以指向的還是同一個Date對象!
4.1clone用法那么我們如何克隆對象呢?無論是淺拷貝還是深拷貝都是這兩步:
克隆的對象要實現Cloneable接口
重寫clone方法,最好修飾成public
淺拷貝:僅僅拷貝了Person對象,而date沒有拷貝!
public class Person implements Cloneable { // 可變的成員變量 private Date date; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
深拷貝:不僅拷貝了Person對象,也拷貝了date成員變量
public class Person implements Cloneable { // 可變的成員變量 public Date date; @Override public Object clone() throws CloneNotSupportedException { // 拷貝Person對象 Person person = (Person) super.clone(); // 將可變的成員變量也拷貝 person.date = (Date) date.clone(); // 返回拷貝的對象 return person; } }4.2clone疑問進一步學習protected
不知道有沒有人跟我有相同的疑問:
我只想要淺拷貝,能不能直接調用該對象.clone()來實現?
比如我現在有個Address對象:
public class Address { private int provinceNo; private int cityNo; private int streetNo; public Address() { } public Address(int provinceNo, int cityNo, int streetNo) { this.provinceNo = provinceNo; this.cityNo = cityNo; this.streetNo = streetNo; } }
下面的代碼你們認為如何?
Address address = new Address(1, 2, 3); address.clone();
我們都知道:
protected修飾的類和屬性,對于自己、本包和其子類可見
可能會想:clone()方法是定義在Object類上的(以protected來修飾),而我們自定義的Address對象隱式繼承著Object(所有的對象都是Object的子類),那么子類調用Object以protected來修飾clone()是完全沒問題的
但是,IDE現實告訴我,這編譯就不通過了!
出現錯誤的原因我立馬就想到:是不是我對protected修飾符出現了偏差?
protected修飾的類和屬性,對于自己、本包和其子類可見,這句話本身是沒有錯的。但是還需要補充:對于protected的成員或方法,要分子類和超類是否在同一個包中。與基類不在同一個包中的子類,只能訪問自身從基類繼承而來的受保護成員,而不能訪問基類實例本身的受保護成員。
上面的代碼就錯在:Address與Object不是在同一個包下的,而Address直接訪問了Object的clone方法。這是不行的。
下面我截兩張圖再來給你們看看(看完圖再看上面的描述,就能理解了):
圖片來源和更多的展開閱讀:https://blog.csdn.net/wangyanguiyiyang/article/details/49800493
五、wait和notify方法wait和notify方法其實就是Java給我們提供讓線程之間通信的API。
按照慣例我們還是來看注釋怎么說吧:
wait方法:
notify方法:
notifyAll()方法:
看完上面的注釋我們可以總結以下的要點:
無論是wait、notify還是notifyAll()都需要由監聽器對象(鎖對象)來進行調用
簡單來說:他們都是在同步代碼塊中調用的,否則會拋出異常!
notify()喚醒的是在等待隊列的某個線程(不確定會喚醒哪個),notifyAll()喚醒的是等待隊列所有線程
導致wait()的線程被喚醒可以有4種情況
該線程被中斷
wait()時間到了
被notify()喚醒
被notifyAll()喚醒
調用wait()的線程會釋放掉鎖
其實總結完上面的并不會有比較深刻的印象,可以嘗試著回答幾個問題來加深對wait()和notify()的理解。
5.1為什么wait和notify在Object方法上?從一開始我們就說了:wait()和notify()是Java給我們提供線程之間通信的API,既然是線程的東西,那什么是在Object類上定義,而不是在Thread類上定義呢?
因為我們的鎖是對象鎖【要是忘記的同學可回顧:Java鎖機制了解一下】,每個對象都可以成為鎖。讓當前線程等待某個對象的鎖,當然應該通過這個對象來操作了。
鎖對象是任意的,所以這些方法必須定義在Object類中
5.2notify方法調用后,會發生什么?上面已經說了,notify會喚醒某個處于等待隊列的線程。
但是要注意的是:
notify方法調用后,被喚醒的線程不會立馬獲得到鎖對象。而是等待notify的synchronized代碼塊執行完之后才會獲得鎖對象
5.3sleep和wait有什么區別?Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權。
主要的區別在于Object.wait()在釋放CPU同時,釋放了對象鎖的控制。
而Thread.sleep()沒有對鎖釋放
參考資料:
https://blog.csdn.net/lingzhm/article/details/44940823
http://www.cnblogs.com/dolphin0520/p/3920385.html
https://www.cnblogs.com/eer123/p/7880789.html
https://www.jianshu.com/p/f4454164c017
六、finalize()方法finalize()方法將在垃圾回收器清除對象之前調用,但該方法不知道何時調用,具有不定性
一般我們都不會重寫它~
一個對象的finalize()方法只會被調用一次,而且finalize()被調用不意味著gc會立即回收該對象,所以有可能調用finalize()后,該對象又不需要被回收了,然后到了真正要被回收的時候,因為前面調用過一次,所以不會調用finalize(),產生問題。
參考資料:
https://segmentfault.com/q/1010000000094660
進階的資料:
https://www.cnblogs.com/Smina/p/7189427.html
http://www.importnew.com/23913.html
https://zhuanlan.zhihu.com/p/29522201
https://zhuanlan.zhihu.com/p/25698745
七、總結總的來說也算是把Object看了一遍了,不至于一下子把它的方法給忘了~~~在學習的過程中也遇到過問題,最明顯的是對protected修飾符又加深了一次理解。
參考資料:
《Java核心技術卷一》
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導航:
https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71104.html
摘要:什么交互都處理不了怎么辦簡化復雜度復雜邏輯后端處理的多線程上面都是的做法,但是也是處理這種問題的一把好手。換一種思路,上面就是利用實現一種偽多線程的概念。 教科書里面的setTimeout 定義很簡單 setTimeout() 方法用于在指定的毫秒數后調用函數或計算表達式。 廣泛應用場景 定時器,輪播圖,動畫效果,自動滾動等等 上面一些應該是setTimeout在大家心中的樣子...
摘要:權威指南第六版關于閉包的說明采用詞法作用域,也就是說函數的執行依賴于變量的作用域,這個作用域是在函數定義時決定的,而不是函數調用時決定的。閉包這個術語的來源指函數變量可以被隱藏于作用域鏈之內,因此看起來是函數將變量包裹了起來。 最近打算換工作,所以參加了幾次面試(國內比較知名的幾家互聯網公司)。在面試的過程中每當被問起閉包,我都會說閉包是作用域的問題?令人驚訝的是幾乎無一例外的當我提到...
摘要:所以說我們的線程最好是交由線程池來管理,這樣可以減少對線程生命周期的管理,一定程度上提高性能。線程池不接收新任務,不處理已添加的任務,并且會中斷正在處理的任務。當所有的任務已終止,記錄的任務數量為,線程池會變為狀態。線程池徹底終止的狀態。 前言 只有光頭才能變強 回顧前面: ThreadLocal就是這么簡單 多線程三分鐘就可以入個門了! 多線程基礎必要知識點!看了學習多線程事半功倍...
摘要:如果構造函數有返回值呢一般情況下構造函數沒有返回值,但是我們依舊可以得到該對象的實例如果構造函數有返回值,憑直覺來說情況應該會不一樣。歡迎光臨小弟博客我的博客原文你真的弄明白了嗎參考再談面向對象編程的實例化與繼承請停止使用關鍵字 好久沒有寫點東西了,總覺得自己應該寫點牛逼的,卻又不知道如何下筆。既然如此,還是回歸最基本的吧,今天就來說一說這個new。關于javascript的new關鍵...
閱讀 1768·2023-04-26 01:44
閱讀 1211·2021-11-12 10:34
閱讀 1578·2021-09-09 09:33
閱讀 1729·2019-08-30 15:44
閱讀 2892·2019-08-30 13:49
閱讀 2190·2019-08-29 15:26
閱讀 943·2019-08-26 13:30
閱讀 1409·2019-08-23 18:15