- “== ”用於判斷的是對象的內存地址
public class ArrayTest {
public static void main(String[] args){
String a = new String("aw");
String b = new String("aw");
System.out.println(a==b);//false
}
}
顯然,儘管 a 與 b 對象的值相同,但是在內存中的地址是不同的,即兩個對象是不一樣的。
再看一個例子:
public class ArrayTest {
public static void main(String[] args){
String a = new String("aw");
String b = new String("aw");
String c= "aa";
String d= "aa";
System.out.println(a.hashCode());
System.out.println(b.hashCode());
System.out.println(c.hashCode());
System.out.println(d.hashCode());
System.out.println(a==b);//false
System.out.println(c==d);//true
}
}
運行結果爲
3126
3126
3104
3104
false
true
可見兩個對象不相同,他們的hashCode值不一定不相同。
2.hashcode
hashcode定義:
hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值,也就是哈希碼,哈希碼並不是完全唯一的,它是一種算法,讓同一個類的對象按照自己不同的特徵儘量的有不同的哈希碼,但不表示不同的對象哈希碼完全不同。在Java中,哈希碼代表對象的特徵。
注意: 有些朋友誤以爲默認情況下,hashCode返回的就是對象的存儲地址,事實上這種看法是不全面的,確實有些JVM在實現時是直接返回對象的存儲地址,但是大多時候並不是這樣,只能說可能存儲地址有一定關聯。
hashCode方法的作用:
增加查詢速度
equals方法執行流程
先比較兩個對象的hashCode,然後比較兩個對象所指向的值
可以對照散列表的數據結構理解,hashcode值相當於桶的索引值,equals方法主要是判斷在相同索引值下,遍歷鏈表的值是否相同。
故可以的得出如下結論:
如果調用equals方法得到的結果爲true,則兩個對象的hashcode值必定相等;
如果equals方法得到的結果爲false,則兩個對象的hashcode值不一定不同;
如果兩個對象的hashcode值不等,則equals方法得到的結果必定爲false;
如果兩個對象的hashcode值相等,則equals方法得到的結果未知。
或者:
1、如果兩個對象相同,那麼它們的hashCode值一定要相同;
2、如果兩個對象的hashCode相同,它們並不一定相同
3.兩個對象不相同,他們的hashCode值不一定不相同。
4.兩個對象的hashCode值不相同,他們一定是兩個不同的對象
**在什麼場景下需要重新實現hashcode和equals這兩個方法。**
1、加入到hashset中的自定義類的對象,爲確保他們不重複,需要對他們的類重寫equals()和hashcode()的方法。
如果不重寫equals,相同內容不同引用的對象會被當做不同的對象被加入到hashset中。
2.在重寫equals方法的同時,必須重寫hashCode方法
兩個對象相等,首先要滿足其hashcode值相等,因爲兩個不同的對象很有可能共用一個hashcode值。然後equals方法來判斷是否相等。只有兩種同時滿足才能確定他們兩個對象是相等的。
注意:equals方法最初是在所有類的基類Object中進行定義的,源碼是
在這裏插入代碼片
public boolean equals(Object obj) {
return (this == obj);
}
由equals的源碼可以看出這裏定義的equals與是等效的(Object類中的equals沒什麼區別),不同的原因就在於有些類(像String、Integer等類)對equals進行了重寫,但是沒有對equals進行重寫的類(比如我們自己寫的類)就只能從Object類中繼承equals方法,其equals方法與就也是等效的,除非我們在此類中重寫equals。
@see https://www.cnblogs.com/zjc950516/p/7877511.html
對象的引用:
1、強引用(StrongReference)
強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。如下:
Object o=new Object(); // 強引用
當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。如果不使用時,要通過如下方式來弱化引用,如下:
o=null; // 幫助垃圾收集器回收此對象
2、軟引用(SoftReference)
如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。
3、弱引用(WeakReference)
當一個對象僅僅被weak reference(弱引用)指向, 而沒有任何其他strong reference(強引用)指向的時候, 如果這時GC運行, 那麼這個對象就會被回收,不論當前的內存空間是否足夠,這個對象都會被回收。
弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
4、虛引用(PhantomReference)
“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之 關聯的引用隊列中。
表格說明
object的finalize()方法
垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。子類重寫 finalize 方法,以配置系統資源或執行其他清除。
finalize的執行過程(生命週期)
首先,大致描述一下finalize流程:當對象變成(GC Roots)不可達時,GC會判斷該對象是否覆蓋了finalize方法,若未覆蓋,則直接將其回收。否則,若對象未執行過finalize方法,將其放入F-Queue隊列,由一低優先級線程執行該隊列中對象的finalize方法。執行finalize方法完畢後,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“復活”
@see java finalize方法總結、GC執行finalize的過程 https://www.cnblogs.com/Smina/p/7189427.html
靜態變量、實例變量、局部變量與線程安全
1.靜態變量:線程非安全。
靜態變量表示所有實例共享的一個屬性,位於方法區,共享一份內存,而成員變量是對象的特殊描述,不同對象的實例變量被分配在不同的內存空間,一旦靜態變量被修改,其他對象均對修改可見,故線程非安全。
2.實例變量:單例模式(只有一個對象實例存在)線程非安全,非單例線程安全。
實例變量爲對象實例私有,在虛擬機的堆中分配,若在系統中只存在一個此對象的實例,在多線程環境下,被某個線程修改後,其他線程對修改均可見,故線程非安全;如果每個線程執行都是在不同的對象中,那對象與對象之間的實例變 量的修改將互不影響,故線程安全。
3.局部變量:線程安全。
局部變量存在於棧內存中,作用的範圍結束,變量空間會自動釋放。由於每個線程執行時將會把局部變量放在各自棧幀的工作內存中,線程間不共享,故不存在線程安全問題。
@see 靜態變量、實例變量、局部變量與線程安全 https://www.cnblogs.com/tonyluis/p/5549149.html
解決hash衝突的幾種方法
1.鏈表法
鏈表法就是將相同hash值的對象組織成一個鏈表放在hash值對應的槽位,如Java中的HashMap
2,不易探測到整個散列表的所有空間(線性探測法除外,但線性探測會出現堆積)
2.開放地址法
開放地址法有個非常關鍵的特徵,就是所有輸入的元素全部存放在哈希表裏,也就是說,位桶的實現是不需要任何的鏈表來實現的,換句話說,也就是這個哈希表的裝載因子不會超過1。它的實現是在插入一個元素的時候,先通過哈希函數進行判斷,若是發生哈希衝突,就以當前地址爲基準,根據再尋址的方法(探查序列),去尋找下一個地址,若發生衝突再去尋找,直至找到一個爲空的地址爲止。所以這種方法又稱爲再散列法。
有幾種常用的探查序列的方法:
①線性探查
dii=1,2,3,…,m-1;這種方法的特點是:衝突發生時,順序查看錶中下一單元,直到找出一個空單元或查遍全表。
(使用例子:ThreadLocal裏面的ThreadLocalMap)
②二次探查
di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 );這種方法的特點是:衝突發生時,在表的左右進行跳躍式探測,比較靈活。
③ 僞隨機探測
di=僞隨機數序列;具體實現時,應建立一個僞隨機數發生器,(如i=(i+p) % m),生成一個位隨機序列,並給定一個隨機數做起點,每次去加上這個僞隨機數++就可以了。
缺點:1,刪除工作很困難,假如要從哈希表 HT 中刪除一個記錄,應將這個記錄所在位置置爲空,但我們只能標上已被刪除的標記,否則,將會影響以後的查找。
2,不易探測到整個散列表的所有空間(線性探測法除外,但線性探測會出現堆積)
3.再散列法
再散列法其實很簡單,就是再使用哈希函數去散列一個輸入的時候,輸出是同一個位置就再次散列,直至不發生衝突位置
缺點:每次衝突都要重新散列,計算時間增加。
4.公共溢出區法
散列表由兩個一維數組組成,一個稱爲基本表,它實際上就是一個散列表。另外一個稱爲溢出表。插入首先在基本表上進行,假如發生衝突,則將同義詞存入溢出表。這樣,可以保證基本表不會發生“堆積”
PS:基本表是不會發生堆積了,那溢出表呢?當進行查找時,查找到溢出表,這是不是又開啓了新一輪的衝突解決?
@see 【java基礎 10】hash算法衝突解決方法 https://www.cnblogs.com/hhx626/p/7534618.html
java創建對象的四種方式
java程序中對象的創建有四種方式:
● 調用new語句創建對象,最常見的一種
● 運用反射手段創建對象,調用java.lang.Class 或者 java.lang.reflect.Constructor 類的newInstance()實例方法
● 調用對象的clone()方法
● 運用序列化手段,調用java.io.ObjectInputStream 對象的 readObject()方法.
@see https://www.cnblogs.com/avivahe/p/5702132.html
參考資料
1.淺談Java中的hashcode方法 https://www.cnblogs.com/dolphin0520/p/3681042.html
2.[轉]Java 的強引用、弱引用、軟引用、虛引用 http://www.cnblogs.com/gudi/p/6403953.html