Object general method

Posted by Wardy7 on Tue, 28 Dec 2021 18:58:08 +0100

Object

overview

public final native Class<?> getClass()

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

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

public final void wait() throws InterruptedException

protected void finalize() throws Throwable {}

equals()

1. Equivalence relation

(1) Reflexivity

x.equals(x); // true

(2) Symmetry

x.equals(y) == y.equals(x); // true

(3) Transitivity

if (x.equals(y) && y.equals(z))
    x.equals(z); // true;

(4) Consistency

The equals() method is called multiple times, and the result remains unchanged

x.equals(y) == x.equals(y); // true

(5) Comparison with null

Calling x.equals(null) on any object x that is not null will result in false

x.equals(null); // false;

2. Equals and==

  • For basic types, = = determines whether two values are equal. Basic types have no equals() method
  • For reference types, = = determines whether two variables refer to the same object, while equals() determines whether the referenced object is equivalent
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

3. Realization

  • Check whether it is a reference to the same object. If so, return true directly
  • Check whether it is the same type. If not, return false directly
  • Transform Object object
  • Determine whether each key field is equal
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() returns a hash value, and equals() is used to determine whether two objects are equivalent. Two equivalent objects must have the same hash value, but two objects with the same hash value are not necessarily equivalent

When overriding the equals() method, you should always override the hashCode() method to ensure that the hash values of the two equivalent objects are also equal

In the following code, two equivalent objects are created and added to the HashSet. We want to treat the two objects as the same and add only one object to the collection. However, because EqualExample does not implement the hasCode() method, the hash values of the two objects are different, resulting in the addition of two equivalent objects to the collection

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

The ideal hash function should be uniform, that is, unequal objects should be evenly distributed over all possible hash values. This requires the hash function to take the values of all fields into account. Each field can be regarded as a bit in r-ary, and then form an r-ary integer. R is generally taken as 31 because it is an odd prime number. If it is an even number, when multiplication overflow occurs, the information will be lost, because multiplying with 2 is equivalent to moving one bit to the left

A number multiplied by 31 can be converted into shift and subtraction: 31 * x = = (x < < 5) - x, and the compiler will automatically optimize it

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}

toString()

Default return ToStringExample@4554617c This form, where the value after @ is the unsigned hexadecimal representation of the hash code

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()

1. cloneable

clone() is the protected method of Object. It is not public. If a class does not explicitly override clone(), other classes cannot directly call the clone() method of the class instance

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'

Overriding clone() yields the following implementation:

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

CloneNotSupportedException was thrown above because CloneExample does not implement the clonable interface

It should be noted that the clone() method is not a method of the clonable interface, but a protected method of Object. The clonable interface only stipulates that if a class does not implement the clonable interface and calls the clone() method, it will throw clonnotsupportedexception

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

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

2. Light copy

The reference type of the copy object and the original object refer to the same object

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

3. Deep copy

The reference types of the copied object and the original object refer to different objects

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

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

Topics: Java Back-end