Object the superclass
Java is a natural object-oriented language. And all the classes that we create by hand, they all inherit from the same class, the Object class.
Take a look at the structure of the Object class
Native method
First, the superclass has a native method
private static native void registerNatives(a);
static {
registerNatives();
}
Copy the code
In Java, functions decorated with native keywords indicate that the implementation of this method is not done in Java. Instead, it is done by C/C++ and compiled into a.ddL file, which is called by Java. The registerNatives() method itself mainly maps methods in C/C++ to native methods in Java to achieve method naming decoupling. At the same time, a static code block is also defined. Therefore, whenever we create Java objects, the system always calls the static code block first, that is, calls the native method. The method is decorated private, indicating that the method is private and cannot be called externally
GetClass method
Using this method, you get a Class instance of the Class, which shows the Java reflection mechanism
HashCode methods
The definition of Baidu Encyclopedia is as follows:
HashCode is not completely unique. It is an algorithm that allows objects of the same class to have different hash codes according to their different characteristics, but does not mean that different object hash codes are completely different. The same is true of how programmers write hash codes.
A Hash table (also called a Hash table) is a data structure that accesses records based on Key values mapped to a location in the table to speed up search.
As a result, Java’s internal hash function can allocate a memory address to each instantiated object and record it in the hash table, which makes it easier to find, create, and compare objects in the program more efficiently.
Write an example to print and see:
public class base {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.hashCode());
System.out.println(apple);
System.out.println(Integer.valueOf("74a14482".16)); }}class Apple {}Copy the code
Print result:
1956725890 p2.Apple@74a14482 1956725890 The process has ended. Exit code 0Copy the code
The hash address of the visible object is a base 10 number, corresponding to the printed native hexadecimal address
The equals method
The Object equals() method is used to compare whether two objects are equal.
The equals() method compares two objects to determine whether they refer to the same object.
You can see that when equals compares two objects for equality, it compares their Hashcodes for equality. Therefore, if you want to override equals, you usually override hashCode as well.
For example, String is not a native data type (such as int, char, double, etc.), but rather an object that Java has rewrapped. String, Integer, etc. override equals to compare values for equality instead of referencing types (hashcode).
For example, String rewraps the equals method:
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;
}
Copy the code
Instanceof is the Reserved Java keyword. It tests whether the object to its left is an instance of the class to its right, returning Boolean data types. If the references are the same, return true; otherwise, the String instance is converted to an array of characters and matched one by one for equality, that is, for matching values.
String also overrides the hashCode method:
public int hashCode(a) {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
Copy the code
The default hash value is 0. Therefore, rewrite the hash formula as follows: hash=s[0]*31^(n-1) + S [1]*31^(n-2) +… + s[n-1]
== and equals
-
== : When comparing basic types, the values of the two types are compared for equality; When comparing reference types, reference references (HashCode) are compared for equality
-
Equals: Uses the source code to compare references to equals. Parts of types such as String and Integer override the equals method to compare values
A few examples:
public class base {
public static void main(String[] args) {
// new two strings with the same content
String a = new String("xxx");
String b = new String("xxx");
System.out.println(a == b); // Compare hash values because they are two different instantiated objects, return false
System.out.println(a.equals(b)); // Compare the contents, both "XXX", return true
// Generate two references
String c = "xxx";
String d = "xxx";
System.out.println(c == d); // Compares the hash value, which is identical because it refers to the same reference, and returns true
System.out.println(c.equals(d)); // Compare the contents, both "XXX", return true}}Copy the code
Equals equals equals equals equals equals equals equals equals equals equals equals equals equals equals equals
Clone method
The Object Clone () method is used to create and return a copy of an Object.
The Clone method is a shallow copy, in which the object referenced by the attribute in the object will only copy the referenced address without reallocating the memory of the referenced object, while the corresponding deep copy will also recreate the referenced object.
According to the source document, the Clone method can only implement a shallow copy, and the class needs to override the Clone method and call super.clone to get the returned object, because the instance method subclass protected by the base class has no access under different packages. In addition, the object class itself does not implement the Cloneable interface, but we write their own class needs to inherit the Cloneable interface, otherwise you will always throw CloneNotSupportedException anomalies.
Write an example:
public class base{
public static void main(String[] args) throws CloneNotSupportedException {
// instantiate a Student object
Student student = new Student(18."Tony");
// Print the content
System.out.println(student);
// Clone student instance
Student anotherStudent = (Student) student.clone();
// Print the clone contentSystem.out.println(anotherStudent); }}class Student implements Cloneable {
int age;
String name;
Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return "Student{" +
"age=" + age +
", name='" + name + '\' ' +
'} '; }}Copy the code
Print result:
Student{age=18, name='Tony'} Student{age=18, name='Tony'Copy the code
Shallow copy and deep copy
Shallow copy example
When the member of the copied object has a reference object, such as when the Student class contains another Teacher object, the cloned object and the cloned object point to the same Teacher reference, so when the Teacher data is changed, the cloned object also changes
Write an example:
public class base {
public static void main(String[] args) throws CloneNotSupportedException {
// instantiate a Teacher object
Teacher teacher = new Teacher(25."JayChou");
// instantiate a Student object
Student student = new Student(18."Tony",teacher);
// Print the content
System.out.println(student);
// Clone student instance
Student anotherStudent = (Student) student.clone();
System.out.println(anotherStudent);
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
// modify teacher data and update student
teacher.setAge(30);
student.setTeacher(teacher);
// Prints the modified student instance and cloned object instanceSystem.out.println(student); System.out.println(anotherStudent); }}class Student implements Cloneable {
int age;
String name;
Teacher teacher;
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
Student(int age, String name, Teacher teacher) {
this.age = age;
this.name = name;
this.teacher = teacher;
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return "Student{" +
"age=" + age +
", name='" + name + '\' ' +
", teacher=" + teacher +
'} '; }}class Teacher implements Cloneable {
int age;
String name;
Teacher(int age, String name) {
this.age = age;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return "Teacher{" +
"age=" + age +
", name='" + name + '\' ' +
'} '; }}Copy the code
Print result:
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} --------------------------------------- Student{age=18, name='fuck', teacher=Teacher{age=30, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=30, Name ='JayChou'}} Process terminated, exit code 0Copy the code
This is the result of a shallow copy, which points to the same reference and causes cascading changes when one of the instances is updated
So instead, implement deep copy so that there is no chain reaction and the clone is completely separated from the cloned object!
Implementing deep copy
Have a general idea:
- Instead of using the Clone method, create a new object and place all the attribute members of the object to be copied
// instantiate a Teacher object
Teacher teacher = new Teacher(25."JayChou");
// instantiate a Student object
Student student = new Student(18."Tony",teacher);
// Print the content
System.out.println(student);
// create a new one!
Student anotherStudent = new Student(18."Tony".new Teacher(25."JayChou"));
System.out.println(anotherStudent);
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
// modify teacher data and update student
teacher.setAge(30);
student.setTeacher(teacher);
// Prints the modified student instance and cloned object instance
System.out.println(student);
System.out.println(anotherStudent);
Copy the code
Print result:
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} --------------------------------------- Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, Name ='JayChou'}} Process terminated, exit code 0Copy the code
- Rewrite the clone method to clone each reference object as well
@Override
protected Object clone(a) throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.setTeacher((Teacher) this.teacher.clone());
return student;
}
Copy the code
Print result:
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} --------------------------------------- Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, Name ='JayChou'}} Process terminated, exit code 0Copy the code
- serialization
There are many ways to serialize, mainly with more tools… Here I use Apache Commons Lang serialization
First, the related classes need to inherit the serialization interface (the interface has no real implementation content, just as a token)
public class base {
public static void main(String[] args) throws CloneNotSupportedException {
// instantiate a Teacher object
Teacher teacher = new Teacher(25."JayChou");
// instantiate a Student object
Student student = new Student(18."Tony",teacher);
// Print the content
System.out.println(student);
// Serialize deep copy
Student anotherStudent = (Student) SerializationUtils.clone(student);
System.out.println(anotherStudent);
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
// Prints serialized content as a byte stream
byte[] res = SerializationUtils.serialize(student);
System.out.println(SerializationUtils.serialize(student));
// Prints deserialization results
System.out.println(SerializationUtils.deserialize(res));
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
// modify teacher data and update student
teacher.setAge(30);
student.setTeacher(teacher);
// Prints the modified student instance and cloned object instanceSystem.out.println(student); System.out.println(anotherStudent); }}class Student implements Serializable {
int age;
String name;
Teacher teacher;
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
Student(int age, String name, Teacher teacher) {
this.age = age;
this.name = name;
this.teacher = teacher;
}
@Override
public String toString(a) {
return "Student{" +
"age=" + age +
", name='" + name + '\' ' +
", teacher=" + teacher +
'} '; }}class Teacher implements Serializable {
int age;
String name;
Teacher(int age, String name) {
this.age = age;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString(a) {
return "Teacher{" +
"age=" + age +
", name='" + name + '\' ' +
'} '; }}Copy the code
Print result:
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} --------------------------------------- [B@50040f0c Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}} --------------------------------------- Student{age=18, name='Tony', teacher=Teacher{age=30, name='JayChou'}} Student{age=18, name='Tony', teacher=Teacher{age=25, Name ='JayChou'}} Process terminated, exit code 0Copy the code
Conclusion: The first method is clumsy, while the second method requires manual rewriting of the Clone method, which is not a wise choice when the object is complex. In contrast, the third one is very convenient and handsome, but has some system overhead due to the complexity of the underlying implementation.
The toString method
When this method is not overridden, a hexadecimal concatenated string of the class name and hash address is returned when the instantiated object is printed. It is recommended that all subclasses override this method for human readability
For example, my Student class overrides this method:
class Student implements Serializable {
int age;
String name;
Teacher teacher;
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
Student(int age, String name, Teacher teacher) {
this.age = age;
this.name = name;
this.teacher = teacher;
}
@Override
public String toString(a) {
return "Student{" +
"age=" + age +
", name='" + name + '\' ' +
", teacher=" + teacher +
'} '; }}Copy the code
Print the object and return human-readable content:
Student{age=18, name='Tony', teacher=Teacher{age=25, name='JayChou'}}
Copy the code
Thread method
Wait (),wait(long),wait(long,int),notify(), and notifyAll() are used to sleep and wake up threads respectively