Take you deeper into Class - deep analysis: reflection from entry to mastery

1. Principle of class

Mencius said: those who win the hearts of the people win the world. In Java, the "human heart" is the "Class". After obtaining the "Class", we can do whatever we want. Let's go deep into the "human heart" to explore the principle of Class.

First, learn how the JVM builds instances.

1.1 JVM build instance

JVM: Java Virtual Machine. The JVM is divided into stack, heap, method area, etc., but these are JVM memory. The memory described in this article refers to JVM memory class # file is a bytecode file through Compiled from java} files.

Knowing the above, we begin to create instances. Let's take creating a Person object as an example:

Person p = new Person()

Simply create an object through "new". What is the process like? See the figure below

It's too rough. Let's refine it.

Comrades, do you find that there are still some differences here? I tell you the difference is that there are more words below than above. You will hit me no (don't hit me in the face).

The rough one is created by "new", while the delicate one is created by "ClassLoader" Class , file generates class , and then creates the object.

In fact, to create an instance through {new} or reflection, you need a {Class} object.

1.2 .class file

The. class , file was mentioned at the beginning of the article. It is a bytecode file Java is the source program. Java programs are cross platform, one-time compilation is executed everywhere, and compilation is to convert from source files to bytecode files.

Bytecode is nothing more than a file composed of 0 and 1.

There is one class:

Check the bytecode file through vim:

What's this? I can't understand it. We don't need to understand it. Anyway, the JVM is right class} files have their own reading rules.

Class 1.3 loader

Remember in the above exquisite figure, we know that it is through the class loader Load the class file into memory. I will write another article to explain the specific content of class loader (the link will be updated here after writing). However, the core method is loadClass(). Just tell it the # name to load, and it will help you load:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 1. Check whether the class has been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 2. Not loaded yet, follow the parent priority loading mechanism (parent delegation mechanism)
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // 3. If the loading is not successful, call findClass()
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

// If you need to override this method, the default is to throw an exception
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

class loader loading class # file is mainly divided into three steps

  1. Check whether the class has been loaded. If so, return directly
  2. This class does not exist at present. Follow the parental delegation mechanism and load Class file
  3. The above two steps fail. Call findClass()

Because the findClass method of ClassLoader throws an exception by default, we need to write a subclass to overwrite it, such as:

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // Read xxx from the specified location through IO stream Class file to get byte array
            byte[] datas = getClassData(name);
            if (null == datas){
                throw new ClassNotFoundException("Class not found:" + name);
            }
            // Call the defineClass() method of the class loader itself to get the class object from the bytecode
            return defineClass(name, datas, 0, datas.length);
        }catch (IOException e){
            throw new ClassNotFoundException("Class not found:" + name);
        }
    }

    private byte[] getClassData(String name) {
        return byte[] datas;
    }

defineClass is a method to obtain Class through bytecode, which is defined by ClassLoader. We don't know how to implement it, because we will eventually call a native method:

    private native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd);

    private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd, String source);

    private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
                                         int off, int len, ProtectionDomain pd,
                                         String source);

Summarize the class loader loading Class} file steps:

  • Get the # Class through the # loadClass() method in the # ClassLoader # Class
  • Find from the cache and return directly
  • It does not exist in the cache. It is loaded through the parental delegation mechanism
  • The above two steps fail. Call {findClass()
    • Get from the specified location through IO stream Get byte array from class file
    • Call the Class loader , defineClass() method to get the , Class , object from the byte array

1.4 Class

The. Class # file has been loaded into memory by the class loader and generates a byte array. The JVM # creates the corresponding class # object according to the byte array.

Next, let's analyze the Class object.

We know that Java objects will have the following information:

  1. Permission modifier
  2. Class name and generic information
  3. Interface
  4. entity
  5. annotation
  6. Constructor
  7. method

The information is on the Internet The Class} file is represented by 0101. Finally, the JVM will The information of the Class , file is saved to Class , in its way.

There must be a field in , Class , to save these information. Let's take a look:

Class - class uses the fields in , ReflectionData , to match with The content mapping of class , maps fields, methods, constructors and interfaces respectively.

The annotation data is mapped through "annotaionData", and others will not be displayed. You can open "IDEA" to view the source code of "Class".

Let's take a look at the methods of Class

1.4.1 constructor

The constructor of Class , Class is private and can only create , Class , objects through , JVM. Therefore, there is the above process of obtaining the # Class # object through the Class loader.

1.4.2 Class.forName

Class. The forname () method also gets the class object through the class loader.

1.4.3 newInstance

The bottom layer of newInstance() is to return a parameterless constructor.

2. Summary

Let's sort out the previous knowledge points:

The key point of reflection is to get Class. How does the system get Class?

It is through ClassLoader # ClassLoader # Load the bytes of the Class # object into the JVM array through the Class # file. How is that kind of loader loaded?

  • Through the # loadClass() method of # ClassLoader #
  • Find from the cache and return directly
  • It does not exist in the cache. It is loaded through the parental delegation mechanism
  • The above two steps fail. Call {findClass()
    • Get from the specified location through IO stream Get byte array from class file
    • Call the Class loader , defineClass() method to get the , Class , object from the byte array

The constructor of Class , Class is private, so you need to obtain , Class through , JVM.

Class.forName() is also a class object obtained through the class loader. The bottom layer of the newInstance} method is also the returned parameterless constructor.

Posted by scrypted on Mon, 02 May 2022 03:14:34 +0300