JDK1.8 source code (1)-java.lang.Object class

Posted May 27, 202016 min read

This series of blogs will introduce the related classes of JDK1.8 version from the source code level, [download address]of JDK8( http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk -8u161-windows-x64.exe? AuthParam = 1519472606_b1014bbce320a78aa9638b63275dfb16).

First introduce the base class of all classes in JDK-java.lang.Object.

The Object class belongs to the java.lang package. All the classes under this package do not need to be manually imported when used. The system will automatically import it during program compilation. The Object class is the base class of all classes. When a class does not directly inherit a class, it inherits the Object class by default, which means that any class inherits this class directly or indirectly. The methods accessible in the Object class are in all classes. Can be called, below we will introduce all the methods in the Object class.

1. Structure diagram of the Object class

Object.class

/*
* Copyright(c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
* /

package java.lang;

/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
*
* @author unascribed
* @see java.lang.Class
* @since JDK1.0
* /
public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    public final native Class <?> getClass();

    public native int hashCode();

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

    protected native Object clone() throws CloneNotSupportedException;

    public String toString() {
        return getClass(). getName() + "@" + Integer.toHexString(hashCode());
    }

    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 {
        if(timeout <0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if(nanos <0 || nanos> 999999) {
            throw new IllegalArgumentException(
                    "nanosecond timeout value out of range");
        }
        if(nanos> 0) {
            timeout ++;
        }
        wait(timeout);
    }

    public final void wait() throws InterruptedException {
        wait(0);
    }

    protected void finalize() throws Throwable {}
}

2. Why do the classes under the java.lang package not need to be imported manually?

I don't know if you noticed it. When we use classes such as Date, we need to import import java.util.Date manually. For example, when we use the File class, we also need to import import java.io.File manually. But we are using Object class, String class, Integer class, etc. without manual import, but can be used directly, why?

Here I will tell you a conclusion:all the classes under the java.lang package do not need to be imported manually.

In addition, we introduce two types of guide packages in Java. There are two methods for guide packages:

, single-type-import(single-type-import), such as import java.util.Date

, type-import-on-demand import, such as import java.util. \ *

Single-type import is relatively easy to understand. By default, the various tools we use for programming are guided by a single-type package. Any class that you need will be imported. This way is to import a specified public class or interface;

Import on demand type, such as import java.util._, you may see the following _, everyone will think it is to import all the classes under the java.util package, but this is not the case, we import according to the name to know that he is in accordance with the name Requirements import, not all classes under the entire package.

The Java compiler will locate the classes that need to be imported from the startup directory(bootstrap), extension directory(extension) and the user class path, and these directories only give the top-level directory of the class. The compiler's class file positioning method can be roughly Understand as the following formula:

Top-level path name package name file name.class = absolute path

For single-type imports, we know the package name and file name, so the compiler can find and locate the desired class file at once. On-demand type import is more complicated. The compiler will arrange and combine the package name and file name, and then find and locate the class file for all possibilities. E.g:

package com;

import java.io. *;

import java.util. *;

If the File class is used in our file, the compiler will search for the File class according to the following steps:

, File //The File class belongs to an unnamed package, which means that the File class has no package statement, and the compiler will first search for the unnamed package

com.File //The File class belongs to the current package, which is the package path of our current compiled class

, java.lang.File //Because the compiler will automatically import the java.lang package, it will also be found from the package

, java.io.File

java.util.File

......

The point to note is that after the compiler finds the java.io.File class, it will not stop the next step of searching, but will search all the possibilities to determine whether there is a class import conflict. Assuming there are three top-level paths at this time, the compiler will perform 3 \ * 5 = 15 searches.

If the compiler finds two classes with the same name after the search is completed, an error will be reported. To delete the class you do not use, and then compile.

So we can draw this conclusion:on-demand type import will definitely not reduce the execution efficiency of Java code, but it will affect the compilation speed of Java code. Therefore, it is best to use single-type import when coding, which can not only improve the compilation speed, but also avoid naming conflicts.

Let s talk about the two types of Java guide packages. Let s go back to why we can use the Object class directly. See the step above to find the class file. The compiler will automatically import the java.lang package, so of course we can use it directly. . As for the reason, because it is used more, it is loaded in advance and saves resources.

3. Class constructor

We know that class constructor is one of the ways to create Java objects. Calling the constructor through the new keyword completes the instantiation of the object, and can also initialize the object accordingly through the constructor. A class must have a constructor. If there is no display declaration, the system will create a parameterless constructor by default. In the JDK Object class source code, the constructor cannot be seen, and the system will automatically add a parameterless constructor. Constructor. We can pass:

Object obj = new Object(); construct an object of class Object.

4. The equals method

Usually many interview questions ask the difference between the equals() method and the == operator. The == operator is used to compare whether the values of the basic types are the same, or whether the references of two objects are equal, and equals is used to compare two objects Is it equal, so it may be more broad to say, how are the two objects equal? How to fix this ruler?
We can look at the equals method in the Object class:

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

It can be seen that in the Object class, the == operator and the equals method are equivalent, and both compare whether the references of two objects are equal. On the other hand, if the references of two objects are equal, then these two The objects must be equal. For an object that we customize, if we do not override the equals method, then when comparing objects, we call the equals method of the Object class, that is, use the == operator to compare two objects. We can look at the overridden equals method in the String class:

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;
    }

String is a reference type. You cannot compare whether references are equal when comparing. The point is whether the contents of the strings are equal. Therefore, the String class defines that two objects are equal in that the string content is the same.

In the Java specification, the use of equals method must follow the following principles:

, Reflexivity:For any non-null reference value x, x.equals(x) should return true.

Symmetry:For any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.

Transitivity:For any non-null reference values x, y and z, if x.equals(y) returns true, and y.equals(z) returns true, then x.equals(z) should return true.

. Consistency:For any non-null reference values x and y, multiple calls to x.equals(y) always return true or always return false, provided that the information used in the equals comparison on the object has not been modified

. For any non-null reference value x, x.equals(null) should return false.

Below we customize a Person class, and then override its equals method to compare two Person objects:

package com.ys.bean;

/**
* Create by vae
* /
public class Person {
private String pname;
private int page;

    public Person() {}

    public Person(String pname, int page) {
        this.pname = pname;
        this.page = page;
    }
    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }
    @Override
    public boolean equals(Object obj) {
        if(this == obj) {//The reference is equal then the two objects are of course equal
            return true;
        }
        if(obj == null ||!(obj instanceof Person)) {//The object is empty or not an instance of the Person class
            return false;
        }
        Person otherPerson =(Person) obj;
        if(otherPerson.getPname(). equals(this.getPname()) && otherPerson.getPage() == this.getPage()) {
            return true;
        }
        return false;
    }

    public static void main(String []args) {
        Person p1 = new Person("Tom", 21);
        Person p2 = new Person("Marry", 20);
        System.out.println(p1 == p2); //false
        System.out.println(p1.equals(p2)); //false

        Person p3 = new Person("Tom", 21);
        System.out.println(p1.equals(p3)); //true
    }

}

By overriding the equals method, we customize the rule that two objects are equal so that the two properties of the Person object are equal, then the objects are equal, otherwise they are not equal. If you do not override the equals method, then always call the equals method of the Object class, that is, use == to compare whether the reference addresses of two objects in the stack memory are equal.

At this time, there is a subclass Man of the Person class, which also overrides the equals method:

package com.ys.bean;

/**
* Create by vae
* /
public class Man extends Person {
private String sex;

    public Man(String pname, int page, String sex) {
        super(pname, page);
        this.sex = sex;
    }
    @Override
    public boolean equals(Object obj) {
        if(! super.equals(obj)) {
            return false;
        }
        if(obj == null ||!(obj instanceof Man)) {//The object is empty or not an instance of the Person class
            return false;
        }
        Man man =(Man) obj;
        return sex.equals(man.sex);
    }

    public static void main(String []args) {
        Person p = new Person("Tom", 22);
        Man m = new Man("Tom", 22, " ");

        System.out.println(p.equals(m)); //true
        System.out.println(m.equals(p)); //false
    }
}

By printing the results, we find that the result obtained by person.equals(man) is true, but the result obtained by man.equals(person) is false, which is obviously incorrect.

The problem appears on the instanceof keyword. For the usage of the instanceof keyword, you can refer to my article: http://www.cnblogs.com/ysocea...

Man is a subclass of Person, and person instanceof Man is of course false. This violates the symmetry we mentioned above.

Actually use the instanceof keyword can not meet the requirements of symmetry. The recommended practice here is to replace the instanceof operator with the getClass() method. The getClass() keyword is also a method in the Object class. Its function is to return the runtime class of an object. We will explain it in detail below.

Then the equals method in the Person class is

public boolean equals(Object obj) {
        if(this == obj) {//The reference is equal then the two objects are of course equal
            return true;
        }
        if(obj == null ||(getClass()! = obj.getClass())) {//The object is empty or not an instance of the Person class
            return false;
        }
        Person otherPerson =(Person) obj;
        if(otherPerson.getPname(). equals(this.getPname()) && otherPerson.getPage() == this.getPage()) {
            return true;
        }
        return false;
    }

The result of printing person.equals(man) is false, and the result of man.equals(person) is also false, satisfying symmetry.

Note:The use of getClass is not absolute. It depends on the situation. After all, the standard for defining whether objects are equal is defined by the programmer himself. And the use of getClass does not meet the definition of polymorphism, such as the AbstractSet abstract class, which has two subclasses TreeSet and HashSet, they use different algorithms to find the collection operation, but no matter which way the collection is implemented, you need to have If you use getClass to rewrite the equals method, then you can't compare two objects of different subclasses equally. And the collection class is special, and its subclasses do not need to define the concept of equality.

So when to use the instanceof operator and when to use getClass() have the following suggestions:

If the subclass can have its own concept of equality, the symmetry requirement will be forced to use getClass for testing.

If there is the concept of superclass determining equality, then you can use instanceof to detect, so that you can compare equal between objects of different subclasses.

Here is a suggestion for a perfect equals method:

  1. The display parameter is named otherObject, which will be converted into another variable called other later.

  2. Determine whether the two object references compared are equal. If the references are equal, it means that they are the same object. Of course, they are equal.

  3. If otherObject is null, directly return false, indicating inequality

  4. Compare whether this and otherObject are the same class:if the semantics of equals changes in each subclass, use getClass detection; if all subclasses have a uniform definition, then use instanceof detection

  5. Convert otherObject to the corresponding class type variable

  6. Finally, compare the attributes of the object. Use == to compare basic types, and equals to compare objects. Returns true if they are all equal, otherwise returns false. Note that if you define equals in a subclass, you must include super.equals(other)

Below we give the writing of the complete equals method in the Person class:

@Override
    public boolean equals(Object otherObject) {
        //1, determine whether the two object references compared are equal, if the references are equal, it means the same object, then of course equal
        if(this == otherObject) {
            return true;
        }
        //2. If otherObject is null, directly return false, indicating unequal
        if(otherObject == null) {//The object is empty or not an instance of the Person class
            return false;
        }
        //3. Compare whether this and otherObject are the same class(note that the following two can only use one)
        //3.1:If the semantics of equals changes in each subclass, use getClass to detect
        if(this.getClass()! = otherObject.getClass()) {
            return false;
        }
        //3.2:If all subclasses have a uniform definition, then use instanceof detection
        if(!(otherObject instanceof Person)) {
            return false;
        }

        //4. Convert otherObject to the corresponding class type variable
        Person other =(Person) otherObject;

        //5. Finally, compare the attributes of the object. Use == to compare basic types, and equals to compare objects. True if they are equal, false otherwise
        //Use the equals method of the Objects tool class to prevent one of the compared two objects from being null and report an error, because null.equals() will throw an exception
        return Objects.equals(this.pname, other.pname) && this.page == other.page;

        //6. Note that if equals is defined in a subclass, it should include super.equals(other)
        //return super.equals(other) && Objects.equals(this.pname, other.pname) && this.page == other.page;

    }

Please note that whenever you override this method, you must usually override the hashCode method to maintain the general convention of the hashCode method, which declares that equal objects must have the same hash code. hashCode is also a method in the Object class, which will be explained in detail later.

5, getClass method

When we introduced the equals method above, we introduced that if the semantics of equals changes in each subclass, then use getClass detection. Why do we say this?

GetClass() in the Object class is as follows, the role is to return the runtime class of the object.

public final native Class <?> getClass();

This is a method modified with native keywords. The details of native keywords are as follows:[ http://www.cnblogs.com/ysocea...] ( http://www.cnblogs.com/ysocean/p /8476933.html)

Here we need to know that we do not need to consider the method decorated with native. The operating system helps us to implement it. The function of this method is to return the runtime class of an object. Through this class object, we can obtain the relevant properties and methods of the runtime class. That is, reflection in Java, all common frameworks are implemented using reflection, and we will not make a detailed description here.

Here is a detailed introduction. The getClass method returns an object's runtime class object, how to understand it? There is another such usage in Java. Obtain the class object of this class through the class name .class. What is the difference between these two usages?

Parent class:Parent.class

public class Parent {}

Subclass:Son.class

public class Son extends Parent {}

Test:

@Test
public void testClass() {
    Parent p = new Son();
    System.out.println(p.getClass());
    System.out.println(Parent.class);
}

Printing results:

Conclusion:class is an attribute of a class and can get the class object of the class at compile time, and getClass() is a method of a class, it is the class object of getting the runtime of the class.

Another thing that needs everyone s attention is that although the getClass() method declaration in the Object class is:public final native Class GetClass(); it returns a Class , But it can be compiled as follows:

Class <? Extends String> c = "" .getClass();

In other words, the return type of the getClass method of a variable of type T is actually Class instead of Class In the getClass method declaration.

This is also stated in the official documentation:[ https://docs.oracle.com/javas...] ( https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html # getClass--)

6, hashCode method

HashCode is defined in the Object class as follows:

public native int hashCode();

This is also a native method declared with native, which returns the hash code of the object, which is a numeric value of type int.

So what is the significance of this method?

We know that there are several collection classes in Java, such as List, Set, and Map. List collection generally stores elements that are ordered and repeatable, elements stored by Set are unordered and not repeatable, and Map collection stores Are key-value pairs.

Earlier we said that to determine whether an element is equal, you can use the equals method, without adding an element, then we use the equals method to determine whether each element in the collection is repeated, but if there are 10,000 elements in the collection, but we add a new one For elements, it is necessary to make 10000 equals method calls, which is obviously very inefficient.

So, Java collection designers used hash tables to achieve. I have introduced the data structure of the hash table. Hash algorithm, also known as hash algorithm, is to directly assign the result of data according to a specific algorithm to an address. This result is generated by the hashCode method. In this way, when a new element is to be added to the collection, the hashCode method of this element is called first, so that it can be located at the physical location where it should be placed at once.

If there is no element at this position, it can be stored directly at this position without any comparison;

If there is already an element at this position, call its equals method to compare with the new element, if it is the same, it will not be saved;

If it is not the same, that is, a situation where the same Hash key causes a conflict, then a linked list is generated at the place of this Hash key, and all objects that generate the same HashCode are placed on this single linked list and connected together(few appear). In this way, the number of times the equals method is actually called is greatly reduced, almost only once or twice.


There are four objects A, B, C, and D, and three values are generated by the hashCode method. Note that the values generated by calling hashCode for the A and B objects are the same, that is, A.hashCode() = B.hashCode() = 0x001, a hash conflict has occurred. At this time, because A was inserted first, when inserting B, we found that B is to be inserted at the position of A, and A has been inserted. At this time, we call the equals method to determine Whether A and B are the same. If they are the same, B will not be inserted. If they are different, B will be inserted after A. So there are the following requirements for equals method and hashCode method:

  1. HashCode requirements
    During the program runtime, as long as the change of the object(field) does not affect the decision result of the equals method, then during this period, no matter how many times the hashCode is called, the same hash code must be returned.

    The hashCode of the two objects that return true through equals call must be the same.

    The hash codes of the two objects that return false through equasl need not be different, that is, the return value of their hashCode method allows the same situation.

Therefore we can get the following inferences:

Two objects are equal, their hashCode must be the same;

Two objects are not equal, their hashCode may be the same;

Two objects with the same hashCode are not necessarily equal;

Two objects with different hashCode must not be equal;

The four inferences can be better understood through the above picture.

There may be doubts about why collections that cannot be repeated are not directly generated by hashCode for each element. If the repetition is the same value, then there is no need to call the equals method to determine whether they are the same?
In fact, when there are not many elements, a unique index value is directly generated through hashCode, and the element can be directly found through this index value, and it can also be judged whether they are the same. For example, the ID stored in the database is ordered. We can directly find an element by ID. If the ID of the newly inserted element already exists, it means that it is duplicate data. This is a perfect method. But the reality is that it is difficult for the stored elements to have such ID keywords, and it is difficult to achieve this unique algorithm for hashCode, and even if it can be realized, but the generated hashCode code is very large, which will be much larger than that of Java. The range that can be expressed takes up a lot of memory space, so it is not considered.

Second, hashCode writing instructions:
The hash codes of different objects should be as different as possible to avoid hash conflicts, that is, the elements obtained by the algorithm should be distributed as uniformly as possible.

, The hash value is an int type, which occupies 4 bytes in Java, that is, the 232th power, to avoid overflow.

In Jger's Integer class, Float class, String class, etc. have rewritten the hashCode method, our custom objects can also refer to these classes to write.

The following is the hashCode source code of JDK String class:

public int hashCode() {
        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;
    }

Remind everyone again, for the Map collection, we can choose the basic types in Java, and the reference type String as the key, because they have rewritten the equals method and hashCode method according to the specifications. But if you use a custom object as the key, you must override the equals method and hashCode method, otherwise unexpected errors will occur.

7, toString method

The source code of this method in JDK is as follows:

    public String toString() {
        return getClass(). getName() + "@" + Integer.toHexString(hashCode());
    }

GetClass(). GetName() is the full class name of the returned object(including the package name), and Integer.toHexString(hashCode()) is a string representation of this hash code returned as a hexadecimal unsigned integer.

When printing an object, the default is to call the toString method, such as System.out.println(person), which is equivalent to System.out.println(person.toString())

8, notify()/notifyAll()/wait()

This is a communication method used between multi-threads. Multithreading will be described in detail later, so I wo n t explain it here.

protected void finalize() throws Throwable {}

This method is used for garbage collection, and is generally automatically called by the JVM, and generally does not require the programmer to manually call this method. The JVM will be described in detail later.

10, registerNatives method

This method is defined in the Object class as follows:

private static native void registerNatives();

This is a local method. In the native introduction, we know that after a class defines a local method, if we want to call the implementation of the operating system, we must also load the local library, but we found that there are many local methods in the Object.class class, but But did not see the loading code of the local library. And this is declared with the private keyword, which can't be called outside the class at all. Let's look at the similar source code about this method:

    static {
        registerNatives();
    }

See the above code, this is clear. The static code block is the content that a class must execute during the initialization process, so this method will be executed when the class is loaded, and the local method is registered through this method.

Reference documents: https://docs.oracle.com/javas...

This series of tutorials is continuously updated, you can search for "IT Coke" on WeChat for the first time. Reply "E-book" has the book material that I specially selected for everyone