1. 程式人生 > >Java基礎--關於Object的一些通用方法

Java基礎--關於Object的一些通用方法

我們 ots exce trac UC 值類型 對象 引用 class

equals(Object obj): 判斷兩對象是否相同(String類重寫了該方法)

1. equals() 與 == 的區別

  • 對於基本類型,== 判斷兩個值是否相等,基本類型沒有 equals() 方法。
  • 對於引用類型,== 判斷兩個實例是否引用同一個對象,而 equals() 判斷引用的對象是否等價。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

2.equals()方法在非空對象引用上實現相等關系:

//自反性
x.equals(x); // true

//對稱性
x.equals(y) == y.equals(x) // true

//傳遞性
if(x.equals(y) && y.equals(z)) {
    x.equals(z); // true;
}

//一致性:多次調用 equals() 方法結果不變
x.equals(y) == x.equals(y); // true

//與null比較:對於任何非空對象x
x.euqals(null); // false;

3.實現

  • 檢查是否為同一個對象的引用,如果是直接返回 true;
  • 檢查是否是同一個類型,如果不是,直接返回 false;
  • 將 Object 實例進行轉型;
  • 判斷每個關鍵域是否相等。
public class EqualExample {
    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object o) {
        if (this
== o) return true; if (o == null || getClass() != o.getClass()) return false; EqualExample that = (EqualExample) o; if (x != that.x) return false; if (y != that.y) return false; return z == that.z; } }

hashCode():返回對象的散列值

hashCode()是一個native方法,而且返回值類型是整型;實際上,該native方法將對象在內存中的地址作為哈希碼返回,可以保證不同對象的返回值不同。相同散列值不等於實例相同,而兩相等的實例散列值必相同。

JDK中對hashCode()方法的作用,以及實現時的註意事項做了說明:

  • hashCode()在哈希表中起作用,如java.util.HashMap;

  • 如果對象在equals()中使用的信息都沒有改變,那麽hashCode()值始終不變;

  • 如果兩個對象使用equals()方法判斷為相等,則hashCode()方法也應該相等;

  • 如果兩個對象使用equals()方法判斷為不相等,則不要求hashCode()也必須不相等;但是開發人員應該認識到,不相等的對象產生不相同的hashCode可以提高哈希表的性能。

hashCode()的作用:

總的來說,hashCode()在哈希表中起作用,如HashSet、HashMap等。

當我們向哈希表(如HashSet、HashMap等)中添加對象object時,首先調用hashCode()方法計算object的哈希碼,通過哈希碼可以直接定位object在哈希表中的位置(一般是哈希碼對哈希表大小取余)。如果該位置沒有對象,可以直接將object插入該位置;如果該位置有對象(可能有多個,通過鏈表實現),則調用equals()方法比較這些對象與object是否相等,如果相等,則不需要保存object;如果不相等,則將該對象加入到鏈表中。

這也就解釋了為什麽equals()相等,則hashCode()必須相等。如果兩個對象equals()相等,則它們在哈希表(如HashSet、HashMap等)中只應該出現一次;如果hashCode()不相等,那麽它們會被散列到哈希表的不同位置,哈希表中出現了不止一次。

實際上,在JVM中,加載的對象在內存中包括三部分:對象頭、實例數據、填充。其中,對象頭包括指向對象所屬類型的指針和MarkWord,而MarkWord中除了包含對象的GC分代年齡信息、加鎖狀態信息外,還包括了對象的hashcode;對象實例數據是對象真正存儲的有效信息;填充部分僅起到占位符的作用, 原因是HotSpot要求對象起始地址必須是8字節的整數倍。

toString():默認返回 ToStringExample@4554617c 這種形式,其中 @ 後面的數值為散列碼的無符號十六進制表示。

public class ToStringExample {
    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}

ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());
//ToStringExample@4554617c

clone():Object 的受保護方法,這意味著,如果一個類不顯式去重寫clone() 就沒有這個方法。

1.cloneable

public class CloneExample {
    private int a;
    private int b;
}

CloneExample e1 = new CloneExample();
CloneExample e2 = e1.clone(); // ‘clone()‘ has protected access in ‘java.lang.Object‘

接下來覆蓋 Object 的 clone() 得到以下實現:

public class CloneExample {
    private int a;
    private int b;

    @Override
    protected CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}
CloneExample e1 = new CloneExample();
try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
java.lang.CloneNotSupportedException: CloneExample

以上代碼拋出CloneNotSupported異常,因為CloneExample 沒有實現 Cloneable 接口。

public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

應該註意的是,clone() 方法並不是 Cloneable 接口的方法,而是 Object 的一個 protected 方法。Cloneable 接口只是規定,如果一個類沒有實現 Cloneable 接口又調用了 clone() 方法,就會拋出 CloneNotSupportedException。

2.深拷貝和淺拷貝

  • 淺拷貝:拷貝實例和原始實例的引用類型引用同一個對象;
  • 深拷貝:拷貝實例和原始實例的引用類型引用不同對象。
public class ShallowCloneExample implements Cloneable {
    private int[] arr;

    public ShallowCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ShallowCloneExample clone() throws CloneNotSupportedException {
        return (ShallowCloneExample) super.clone();
    }
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222

public class DeepCloneExample implements Cloneable {
    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

使用 clone() 方法來拷貝一個對象即復雜又有風險,它會拋出異常,並且還需要類型轉換。Effective Java 書上講到,最好不要去使用 clone(),可以使用拷貝構造函數或者拷貝工廠來拷貝一個對象。

public class CloneConstructorExample {
    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}

CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

Java基礎--關於Object的一些通用方法