java裏的Object

  1. “== ”用於判斷的是對象的內存地址
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

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