天天看點

JAVA研發面試題(基礎)Object中的方法-clone/hashCode/equals/tostring/wait-notify-notifyall/finalizeObject類的方法

JAVA研發面試題(基礎)

JAVA基礎(目錄):
Object類的方法,逐個解釋一下(clone,hashCode,equals,wait,finalize,notify)
Java的Exception類型 
Integer和int有啥差別,integer中有哪些特殊的函數?
說一下String實作 intern 
final 關鍵字 
序列化,遠端方法調用
           

Object類的方法

public class Object {

    private static native void registerNatives();
    
    static {
        registerNatives();
    }
    
    public final native Class<?> getClass();
    
    public native int hashCode();
	
	public boolean equals(Object obj) {
        return (this == obj);
    }
    
	protected native Object clone() throws CloneNotSupportedException;

	public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    public final native void notify();
    public final native void notifyAll();
    
	public final native void wait(long timeout) throws InterruptedException;

	public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
    public final void wait() throws InterruptedException {
        wait(0);
    }
    
	protected void finalize() throws Throwable { }
}
           

隻有繼承了Object類的對象

clone–native方法/可以重寫

protected native Object clone() throws CloneNotSupportedException;
           

基本資料類型(八種:byte-1個位元組、short-2個位元組、int-4個位元組、long-8個位元組、float-4個位元組、double-8個位元組、boolean-4個位元組/數組1個位元組、char-2個位元組)

boolean 4個位元組理由來源是《Java虛拟機規範》一書中的描述:“雖然定義了boolean這種資料類型,但是隻對它提供了非常有限的支援。在Java虛拟機中沒有任何供boolean值專用的位元組碼指令,Java語言表達式所操作的boolean值,在編譯之後都使用Java虛拟機中的int資料類型來代替,而boolean數組将會被編碼成Java虛拟機的byte數組,每個元素boolean元素占8位”。這樣我們可以得出boolean類型占了單獨使用是4個位元組,在數組中又是1個位元組。顯然第三條是更準确的說法,那虛拟機為什麼要用int來代替boolean呢?為什麼不用byte或short,這樣不是更節省記憶體空間嗎。大多數人都會很自然的這樣去想,我同樣也有這個疑問,經過查閱資料發現,使用int的原因是,對于當下32位的處理器(CPU)來說,一次處理資料是32位(這裡不是指的是32/64位系統,而是指CPU硬體層面),具有高效存取的特點。 (本段話來自https://www.jianshu.com/p/2f663dc820d0)

/*
需要實作Cloneable接口,否則抛出異常:
Exception in thread "main" java.lang.NullPointerException
 */
public class ObjectCloneTest implements Cloneable{
    int a = 0;
    int[] b = {0,1,2,3};
    public static void main(String [] args){
        ObjectCloneTest obj = new ObjectCloneTest();
        ObjectCloneTest oo=null;
        try {
            oo = (ObjectCloneTest)obj.clone();
        }catch(CloneNotSupportedException e){
            System.out.println("clone出錯" + e.getMessage());
        }
        obj.a = 1;
        obj.b = new int[]{01,11,21,31};
        System.out.println(" 基本資料類型 " + oo.a +" clone的是值  "+ obj.a);
        System.out.println(" 引用資料類型 " + oo.b +" clone的是位址--對象的引用 "+ obj.b);

        int a=0;
        //a.clone();基本資料類型沒有object的方法

        int[] aa = new int[]{0,0,0};
        int[] bb = aa.clone();
        System.out.println("數組  " + aa.toString() + " clone的是值而不是引用 " + bb.toString());
        aa[1] = 2;
        System.out.println("原數組變化  " + aa[1] + " clone數組不變 " + bb[1]);
    }
}

輸出:
基本資料類型 0 clone的是值  1
引用資料類型 [[email protected] clone的是位址--對象的引用 [[email protected]
數組  [[email protected] clone的是值而不是引用 [[email protected]
原數組變化  2 clone數組不變 0
           

淺複制:對于基本資料類型來說,淺複制和深複制效果相同,都是複制的值,對于引用資料類型,object預設方法就是淺複制,即複制的是引用對象的位址。

深複制:對于引用資料類型,深複制要實作将每個引用類型裡面的各個基本類型都複制一遍,即深入到每個屬性裡面的基本類型都拷貝一遍。我們可以重寫對象的clone方法,以實作引用對象的深複制。

@Override
    protected Object clone() throws CloneNotSupportedException {
        ObjectCloneTest ooo = new ObjectCloneTest();
        ooo.a = this.a;
        int [] b = new int [this.b.length];
        for(int i=0;i<this.b.length;i++){
            b[i] = this.b[i];
        }
        ooo.b = b;
        return ooo;
    }
           

hashCode–native方法/可以重寫

public native int hashCode();
           

底層調用了c++實作的本地方法庫,hashCode()是一個native方法,傳回值類型是整數;

看了很多部落格,沒有看到有說c++是怎麼實作的,之後了解了再補上

我們就先這樣了解吧:這個native方法将對象在記憶體中的位址作為哈希碼傳回,保證了不同對象的傳回值不同。

擴充:

基本上兩個相同的對象,equals方法傳回相同,則hashCode的傳回值也最好相同;

但是當兩個對象的hashCode的傳回值相同,還要再去equals比較一下确認是否相同。

這個思想可以參考hashmap判斷兩個對象相同的源碼(如果你重寫了equals()方法,那麼一定要記得重寫hashCode()方法)

通常我們如果要用到hashcode都會重寫這個方法

@Override
    public int hashCode() {
        int result = 17;
        result = result * 31 + b.hashCode();
        result = result * 31 + a;
        return result;
    }//(自動生成的)
           

equals–可重寫

public boolean equals(Object obj) {
        return (this == obj);
    }
           

可以從源碼中看出:equals方法直接使用的是“ == ” 來實作的 ( “==”和clone方法類似,對于基本資料類型比較的是值是否相等,對于引用資料類型比較的是引用類型的位址是否相等)

我們可以重寫這個方法來實作比較一個引用對象的各個屬性/單個屬性 相等來代替對象相等。

@Override
    public boolean equals(Object obj) {
        return this.a==obj.a;
    }
           

String重寫了equals方法,使得最終比較的是各個字元相等即為相等

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
           

擴充:

一般重寫equals方法就要重寫hashCode方法

建議使用自動生成的

JAVA研發面試題(基礎)Object中的方法-clone/hashCode/equals/tostring/wait-notify-notifyall/finalizeObject類的方法
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    ObjectCloneTest that = (ObjectCloneTest) o;
    return a == that.a &&
            Arrays.equals(b, that.b);
}
@Override
public int hashCode() {
    int result = 17;
    result = result * 31 + b.hashCode();
    result = result * 31 + a;
    return result;
}
           

為什麼重寫equals一定要重寫hashcode?

現在有兩個Student對象:

Student s1=new Student("小明",18);

Student s2=new Student("小明",18);
           

此時s1.equals(s2)一定傳回true

假如隻重寫equals而不重寫hashcode,那麼Student類的hashcode方法就是Object預設的hashcode方法,由于預設的hashcode方法是根據對象的記憶體位址經雜湊演算法得來的,顯然此時s1!=s2,故兩者的hashcode不一定相等。

然而重寫了equals,且s1.equals(s2)傳回true,根據hashcode的規則,兩個對象相等其哈希值一定相等,是以沖突就産生了,是以重寫equals一定要重寫hashcode,而且從Student類重寫後的hashcode方法中可以看出,重寫後傳回的新的哈希值與Student的兩個屬性有關。

以下是關于hashcode的一些規定:

兩個對象相等,hashcode一定相等

兩個對象不等,hashcode不一定不等

hashcode相等,兩個對象不一定相等

(例子及下面解釋來自https://blog.csdn.net/xl_1803/article/details/80445481 )

toString-可重寫

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
           

getClass()是native方法,toString()方法調用了hashCode方法,傳回記憶體位址,可以重寫傳回具體的屬性值

建議自動生成:

@Override
    public String toString() {
        return "ObjectCloneTest{" +
                "a=" + a +
                ", b=" + Arrays.toString(b) +
                '}';
    }
           

wait/notify/notifyAll–本地方法/不可重寫(final)

finalize-可重寫,子類可通路

protected void finalize() throws Throwable { }
           

垃圾對象/不可達對象:沒有引用的對象

可達對象:有引用引着的對象

此方法主要用于GC在回收垃圾對象時給對象一次“等一下”的機會,看看有沒有可能複活

GC在回收這個類的執行個體對象時,先會調用這個方法,如果重寫了這個方法,并為其添加了新的引用,則對象就不再是不可達對象了,GC就不會将其加入回收隊列,但是下一次失去引用時就沒有“等一下”機會了,此方法每個對象僅能調用一次,下一次會直接加到回收隊列中。

部落客小白,如有哪塊寫的不妥,歡迎評論~~

參考資料:

https://blog.csdn.net/xl_1803/article/details/80445481

https://baijiahao.baidu.com/s?id=1567910969491375&wfr=spider&for=pc