Object
The Object class is the root class of the class hierarchy. Each class uses Object as its superclass. Each class directly or indirectly inherits from the Object class.
Common methods in Object include:
public int hashCode() //Returns the hash code value of the object. // Note: the hash value is a value calculated according to the hash algorithm. This value is related to the address value, but not the actual address value. public final Class getClass() //Returns the runtime class of this Object public String toString() //Returns a string representation of the object. protected Object clone() //Create and return a copy of this object. This method can be overridden protected void finalize() // This method is called by the object's garbage collector when the garbage collector determines that there are no more references to the object. Used for garbage collection, but when is uncertain.
equals() method
1. Equivalence relation
I reflexivity
x.equals(x); // true
Ⅱ symmetry
x.equals(y) == y.equals(x); // true
III transitivity
if (x.equals(y) && y.equals(z)) x.equals(z); // true;
Ⅳ consistency
The equals() method is called multiple times, and the result remains unchanged
x.equals(y) == x.equals(y); // true
V comparison with null
Calling x.equals(null) on any object x that is not null will result in false
x.equals(null); // false;
2. Equivalence and equality
For primitive types, = = determines whether two values are equal. Primitive 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 yes, return true directly;
Check whether it is the same type. If not, return false directly;
Transform the 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; //Check whether it is a reference to the same object. If yes, return true directly; if (o == null || getClass() != o.getClass()){ //Check whether it is the same type. If not, return false directly return false; } // Transform Object object EqualExample that = (EqualExample) o; // Determine whether each key field is equal. if (x != that.x) return false; if (y != that.y) return false; return z == that.z; } }
hashCode() method
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 into account the values of all fields. You can treat each field as a bit in r-ary, and then form an r-ary integer. R is generally 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() method
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; } } Copy to clipboardErrorCopied ToStringExample example = new ToStringExample(123); System.out.println(example.toString()); Copy to clipboardErrorCopied ToStringExample@4554617c
clone() method
- 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; } Copy to clipboardErrorCopied 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; // CloneExample inherits Object by default @Override public CloneExample clone() throws CloneNotSupportedException { return (CloneExample)super.clone(); } } Copy to clipboardErrorCopied CloneExample e1 = new CloneExample(); try { CloneExample e2 = e1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } Copy to clipboardErrorCopied java.lang.CloneNotSupportedException: CloneExample
CloneNotSupportedException is 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 a clonnotsupportedexception.
public class CloneExample implements Cloneable { private int a; private int b; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
- Shallow 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(); } } Copy to clipboardErrorCopied // The reference type of the copy object and the original object refer to the same object. ShallowCloneExample e1 = new ShallowCloneExample(); ShallowCloneExample e2 = null; try { e2 = e1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } e1.set(2, 222); System.out.println(e1.get(2)); // 222 System.out.println(e2.get(2)); // 222
- Deep copy
The reference types of the copy 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(); // create new object result.arr = new int[arr.length]; for (int i = 0; i < arr.length; i++) { result.arr[i] = arr[i]; } return result; } } Copy to clipboardErrorCopied DeepCloneExample e1 = new DeepCloneExample(); DeepCloneExample e2 = null; try { e2 = e1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } e1.set(2, 222); System.out.println(e1.get(2)); // 222 System.out.println(e2.get(2)); // 2
- Alternative to clone()
Using the clone() method to copy an object is complex and risky. It will throw exceptions and require type conversion. As mentioned in the Effective Java book, it is better not to use clone(). You can use a copy constructor or a copy factory to copy an object.
public class CloneConstructorExample { private int[] arr; public CloneConstructorExample() { //Constructor arr = new int[10]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } } public CloneConstructorExample(CloneConstructorExample original) { // copy constructor 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]; } } Copy to clipboardErrorCopied CloneConstructorExample e1 = new CloneConstructorExample(); CloneConstructorExample e2 = new CloneConstructorExample(e1); e1.set(2, 222); System.out.println(e1.get(2)); // 222 System.out.println(e2.get(2)); // 2