Java: Detailed explanation of the Object class

First of all, we all know that all Java classes inherit from the superclass [Object], which means that all classes are subclasses of Object. This is implicit, and we don't need to point it out.

We define a class A.

class A {
	public A() {
		super();
	}
}

The code is not difficult to understand, that is, the constructor of class A is written. But what needs attention here is the super keyword, which calls the constructor of the parent class. We can follow up and we will jump to the Object class. That is to say, although class A did not explicitly write out the inheritance of Object, Java still let it inherit the Object class.

The Object class has many benefits, in this article, I will talk about it slowly.
We first look at the main methods of the Object class

protected Object clone() creates and returns a copy of this object.
boolean equals(Object obj) indicates whether some other object is "equal" to this object.
protected void finalize() When the garbage collector determines that there are no more references to the object, this method is called by the object's garbage collector.
Class<?> getClass() returns the runtime class of this Object.
int hashCode() returns the hash code value of the object.
String toString() returns the string representation of the object.

Of course, there are some wait(), waitAll() and other methods, which are related to threads, so I won't mention them.

1. Let's take a look at the clone() method
first : First, the clone() method, the subclass of Object is not directly usable, so we need to rewrite it. Please pay attention to the difference between rewriting and reloading. Overriding is the method of rewriting the parent class . Overloading is to write multiple methods with the same method name and different parameter types or number of parameters or return types.
The clone() method is used for deep copy.
Let's first look at the difference between shallow copy and deep copy.
Deep copy: Copy two objects with the same content but completely different reference addresses.
Shallow copy: created two objects with exactly the same address as the original object reference.

Typical example of shallow copy:

public class Main {
	public static void main(String[] args) {
		A a = new A("Aiden");
		A b = a;
		System.out.println(a);
		System.out.println(b);
		System.out.println(a.name.hashCode());
		System.out.println(b.name.hashCode());
	}
}

class A {
	String name;
	public A(String name) {
		this.name = name;
	}
}

Let's look at the output:

test.A@2a139a55
test.A@2a139a55
63256261
63256261

It can be seen that the output content of object a and object b is the same. @ Indicates the class of the object mapping, @ indicates the hash address of the object. The hash code of name is also the same.

More often, we need to deep copy, that is, copy two objects with the same content but different reference addresses. We can rewrite the clone() method to achieve it.
The following is a simple implementation.

public class Main {
	public static void main(String[] args) {
		A a = new A("Aiden");
		A b = a.clone();
		System.out.println(a);
		System.out.println(b);
		System.out.println(a.name.hashCode());
		System.out.println(b.name.hashCode());
	}
}

class A {
	String name;
	public A(String name) {
		this.name = name;
	}
	
	@Override
	protected A clone() {
		String name = new String(this.name);
		return new A(name);
	}
}

Let's take a look at the output:

test.A@2a139a55
test.A@15db9742
63256261
63256261

Let's see, the references of the two objects are indeed different. But why is the initial hash code of the content name of the two objects still the same? so amazing. This is because String is a constant. All the content is stored in the constant pool. Every assignment will be found in the constant pool. If it is found, the address that matches the content will be assigned to name. If it is not found, a new memory will be created. Used to store the content you want to assign.

2. Boolean equals(Object obj) method
This method is used to compare whether two objects are the same. **The same definition includes the same class and the same hash code. **We can follow up the equals() method in the Object class.

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

If you don't override the equals() method, equals is the function of the == symbol.

A a = new A("Aiden");
A b = new A("Aiden");
System.out.println(a.equals(b));
		
b = a;
System.out.println(a.equals(b));

Output result:

false
true

However, we often see that if String objects want to compare content, they use the equals() method, which means that the implementation of the String class must override the equals() method. We can look at the source code:

public boolean equals(Object anObject) {
    if (this == anObject) { // 如果两个对象的引用相同,则返回true
        return true;
    }
    if (anObject instanceof String) { // 如果anObject是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;
}

The source code is still very simple, and the comments are also written, so I won't say more.

3. Protected void finalize()
This method is used to recycle "garbage". When the garbage collector determines that there are no more references to the object, this method is called by the object's garbage collector. For details, please refer to the blog post: click for reference

4. int hashCode() method
This method returns the hash code of the object. Regarding the definition of hash code, this is what the official said

hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。   
  
hashCode 的常规协定是:   
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。   
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。   
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。   
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)   
  
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

The official definition of the hash code of our object is best to be inconsistent. In this way, the equals() method can be different.
Let's first look at what happens when two objects with the same hash code are compared. We have rewritten the hashCode() method and equals() method.

import java.util.HashSet;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		A a = new A("Aiden");
		A b = new A("Aiden");
		System.out.println(a == b);
		System.out.println(a.equals(b));
		
		Set<A> set = new HashSet<>();
		set.add(a);
		set.add(b);
		System.out.println(set.size());
	}
}

class A {
	String name;
	public A(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		return 0;
	}
	
	@Override
	public boolean equals(Object object) {
		if (object == null) 
			return false;
		if(object == this)
			return true;
		if (object instanceof A == false)
			return false;
		A b = (A)object;
		if(name.equals(b.name))
			return true;
		return false;
	}
}

We use the Set object to check whether two objects are hashed or not. Let's look at the specific output.

false
true
[test.A@0]

The first line outputs false, of course, the == sign is to compare the references of two objects.
The second line outputs true because the equals() method is overridden to compare whether the names of the two objects are the same.
The third line of output has only one content, which means that when HashSet is adding, it compares the hash codes of the two objects. If the hash code already exists in the Set set, it will not be added. If it does not exist in the Set collection, join it.

It seems that hash codes are widely used in HashMap, HashSet, and Hashtable. But how can we implement multiple objects with different hash codes?
Here is a general scheme for rewriting hashCode():

  1. Initialize an integer variable and assign a non-zero constant value to this variable, such as int result = 17;
  2. Select all fields used for comparison in the equals method, and then calculate the attributes of each field:
    (1) If it is a boolean value, calculate f? 1:0
    (2) If it is byte\char\short\int, then Calculate (int)f
    (3) If it is a long value, then calculate (int)(f ^ (f >>> 32))
    (4) If it is a float value, then calculate Float.floatToIntBits(f)
    (5) If it is Double value, calculate Double.doubleToLongBits(f), and then return the result is long, then use rule (3) to process long, get int
    (6) If it is an object application, if the equals method adopts a recursive call comparison method, Then the method of recursively calling hashCode is also adopted in hashCode. Otherwise, you need to calculate a normal form for this field. For example, when the value of this field is null, then the hashCode value is 0
    (7) If it is an array, then each element needs to be treated as a separate field. If you are using a JDK version 1.5 and above, there is no need to re-traverse the array yourself. The java.util.Arrays.hashCode method contains 8 basic types of arrays and hashCode calculations for referenced arrays. The algorithm is the same as above.
public static int hashCode(long a[]) {  
    if (a == null)  
        return 0;  
  
    int result = 1;  
    for (long element : a) {  
        int elementHash = (int)(element ^ (element >>> 32));  
        result = 31 * result + elementHash;  
    }  
  
    return result;  
}  

5. String toString() returns the string representation of the object.
We can rewrite this method, or we can rewrite it. If it is not rewritten, it will generally return a string in the format of "className@hashCode". That is to return the class name + "@" + hash code.
In fact, we call the a.toString() method by default when we are in System.out.println(a);, so the format just now will be output.

Guess you like

Origin blog.csdn.net/new_Aiden/article/details/50997217