Java provides a reflection mechanism that allows you to examine class information at run time

Java class loading

Java loads a class when it really needs to be used, rather than loading all classes at startup. Since most users only use part of the program’s resources, loading some resources when they need some functionality makes system resources more efficient.

Loading a class involves reading binary data from a class’s.class file into memory, placing it in the Jvm’s method area, and then creating a Java.lang. class object in the heap that encapsulates the class’s data structure in the method area. The end product of Class loading is the Class object in the heap, which encapsulates the Class’s data structure in the method area and provides the Java programmer with an interface to access the data structure in the method area.


All types in Java include primitive types (
int.
long.
floatAnd so on), even arrays have objects of Class Class associated with them.

Class objects are automatically generated by the Jvm, which automatically generates a Class object for a Class whenever it is loaded

Class object

Instance. GetClass ()

Get the corresponding Class Object for each instance via Object’s getClass()

String name = "hello";

Class stringClass = name.getClass();

System.out.println("Class name :" + stringClass.getName());

System.out.println("Interface or not :" + stringClass.isInterface());

System.out.println("Basic type or not :" + stringClass.isPrimitive());

System.out.println("Array or not :" + stringClass.isArray());

System.out.println("Parent name :" + stringClass.getSuperclass().getName());

Copy the code
The name of the class:java.lang.String

Interface or not:false

Whether it is a basic type:false

Array or not:false

Name of the parent class:java.lang.Object

Copy the code

The name of the class. The class

You can also get the String Class object directly using the following method

Class stringClass = String.class;

Copy the code

Class.forName()

In some applications, you can’t know in advance what classes the user will load. You can use Class’s static forName() method to load classes dynamically

Class c = Class.forName(args[0]);

System.out.println("Class name :" + c.getName());

System.out.println("Interface or not :" + c.isInterface());

System.out.println("Basic type or not :" + c.isPrimitive());

System.out.println("Array or not :" + c.isArray());

System.out.println("Parent name :" + c.getSuperclass().getName());

Copy the code
$ java Demo1 java.util.Scanner  

The name of the class:java.util.Scanner

Interface or not:false

Whether it is a basic type:false

Array or not:false

Name of the parent class:java.lang.Object

Copy the code

There are two versions of class.forname (). The first version specifies only the fully qualified Class name, while the other version lets you specify the Class name, whether to execute a static code block at load time, and execute a ClassLoader.

static Class forName(String name, boolean initialize, ClassLoader loader)

Copy the code
ClassLoader loader = Thread.currentThread().getContextClassLoader();

// The class.forname () load Class executes the initialization block by default

Class.forName("Test2");

// class.forname () loads the Class. The second argument controls whether the initialization block is executed

Class.forName("Test2".false, loader);



class Test2 {

    static {

        System.out.println("Static initialization block executed!");

    }

}

Copy the code

Get information from the Class object

The Class object represents the Class being loaded. Once you get the Class object, you can get information about the Class, including package, constructor, field, method, etc. And for every kind of information, there is a corresponding category

  • package: java.lang.reflect.Package
  • constructor: java.lang.reflect.Constructor
  • field: java.lang.reflect.Field
  • method: java.lang.reflect.Method
Class c = Class.forName(args[0]);

System.out.println("Package information package:" + c.getPackage());

System.out.println("Class modifier modifier:" + c.getModifiers());

System.out.println("Constructor :");

Arrays.stream(c.getDeclaredConstructors()).forEach(System.out::println);

System.out.println("Member variable fields:");

Arrays.stream(c.getDeclaredFields()).forEach(System.out::println);

Copy the code
$ java Demo1 java.util.ArrayList

Package information Package: Package java.util

Class modifier modifier:1

A constructorconstructor:

public java.util.ArrayList(java.util.Collection)

public java.util.ArrayList()

public java.util.ArrayList(int)

Member variable Fields:

private static final long java.util.ArrayList.serialVersionUID

private static final int java.util.ArrayList.DEFAULT_CAPACITY

private static final java.lang.Object[] java.util.ArrayList.EMPTY_ELEMENTDATA

private static final java.lang.Object[] java.util.ArrayList.DEFAULTCAPACITY_EMPTY_ELEMENTDATA

transient java.lang.Object[] java.util.ArrayList.elementData

private int java.util.ArrayList.size

private static final int java.util.ArrayList.MAX_ARRAY_SIZE

Copy the code

ClassLoader ClassLoader

Java loads classes only when they are needed. In Java, classes are loaded by the Class Loader.

When you try to execute the Java XXX command, Java will try to find the JRE installation directory, look for jVM.dll, and start the JVM and initialize it. Then produce BootstrapLoader, the Bootstrap Loader will load the Extended Loader, and set the Extended the parent for BootstrapLoader Loader, The Bootstrap Loader then loads the Application Loader and sets the parent of the Application Loader to Extended Loader

Start the class loader

BootstrapLoader searches for classes specified in sun.boot.library.path, which you can obtain using System.getProperty(“sun.boot.library.path”)

/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib

Copy the code

Extend the class loader

The Extended Loader(Sun.misc.launcher $ExtClassLoader) is written in Java and searches for the category specified in the system parameter java.ext.dirs, This can be obtained via System.getProperty(“java.ext.dirs”)

/Users/dsying/Library/Java/Extensions:

/Library/Java/JavaVirtualMachines/jdk18.. 0_151.jdk/Contents/Home/jre/lib/ext:

/Library/Java/Extensions:

/Network/Library/Java/Extensions:

/System/Library/Java/Extensions:/usr/lib/java

Copy the code

Application class loader

The Application Loader (sun.misc.Launcher$AppClassLoader) is written in Java and searches for the category specified in the system parameter java.class.path. This can be obtained via System.getProperty(“java.class.path”), and the classpath can be set with the -cp parameter when executing the. Class bytecode file using the Java XXX command

Java - cp. / classes SomeClass

Copy the code

Relationships between class loaders

ClassLoader loader Thread.currentThread().getContextClassLoader();

// sun.misc.Launcher$AppClassLoader@18b4aac2 Application class loader

System.out.println(loader);

// sun.misc.Launcher$ExtClassLoader@610455d6 Extension class loader

System.out.println(loader.getParent());

// Bootstrap ClassLoader starts the ClassLoader (C)

System.out.println(loader.getParent().getParent());

Copy the code
sun.misc.Launcher$AppClassLoader@18b4aac2

sun.misc.Launcher$ExtClassLoader@610455d6

null

Copy the code

There are three ways to load a class:

  1. The command line startup application is initialized by the JVM
  2. throughClass.forName()Method dynamic loading
  3. throughClassLoader.loadClass()Method dynamic loading
Class.forname () and classLoader.loadClass ()
  • Class.forName()In addition to loading the class’s.class file into the JVM, the class is interpreted, executing the static block of the class;
  • ClassLoader.loadClass()Class files are loaded into the JVM. Static blocks are executed only at newInstance.
  • Class.forName(name, initialize, loader)Parameterized functions can also control whether static blocks are loaded. And only the newInstance() method is invoked to create objects of the class using the call constructor.

JVM class loading mechanism

  • Overall responsible forWhen a Class is loaded by a Class loader, other classes that the Class depends on and references are also loaded by the Class loader, unless it is shown to be loaded using another Class loader
  • Commissioned by the parent class, first let the parent class loader try to load the class, and only try to load the class from its own classpath if the parent class loader cannot load the class
  • Caching mechanismsWhen a program needs to use a Class, the Class loader first looks for the Class in the cache. Only when the cache does not exist, the system will read the binary data corresponding to the Class, convert it into a Class object, and store it in the cache. Is that why you have to restart the JVM to make Class changes to take effect

Parental delegation model

The workflow of the parental delegation model is as follows: If a class loader received the request of the class loading, it won’t try to load the first class, but give parent to complete the request, in turn up, as a result, all of the class loading request should be passed to the top finally start the class loader, only when the parent loader in the scope of its search did not find the required class, is unable to complete the loading, The child loader will try to load the class itself.

Parent delegate mechanism:

  1. whenAppClassLoaderWhen a class is loaded, it first does not attempt to load the class itself. Instead, it delegates the class-loading request to the parent classloaderExtClassLoaderTo complete.
  2. whenExtClassLoaderWhen a class is loaded, it doesn’t try to load the class itself in the first place. Instead, it delegates the class-loading request toBootStrapClassLoaderTo complete.
  3. ifBootStrapClassLoaderLoading failed (for example in$JAVA_HOME/jre/libClass was not found inExtClassLoaderTo try to load;
  4. ifExtClassLoaderAlso failed to load, will be usedAppClassLoaderTo load, ifAppClassLoaderIf the load fails, an exception is reportedClassNotFoundException.

ClassLoader source code:

publicClass<? > loadClass(String name)throws ClassNotFoundException {

    return loadClass(name, false);

}

protected synchronizedClass<? > loadClass(String name,boolean resolve)throws ClassNotFoundException {

    // First check if the type is already loaded

    Class c = findLoadedClass(name);

    if (c == null) {

        // If it is not loaded, delegate to the parent class loader or delegate to the start class loader

        try {

            if(parent ! =null) {

                // Delegate to the parent class loader if there is one

                c = parent.loadClass(name, false);

            } else {

                // If there is no parent Class loader, check whether the Class was loaded by the bootloader, by calling the native Class findBootstrapClass(String name).

                c = findBootstrapClass0(name);

            }

        } catch (ClassNotFoundException e) {

                // Call its own loading function if neither the parent class loader nor the launcher class loader can complete the loading task

                c = findClass(name);

            }

        }

    if (resolve) {

        resolveClass(c);

    }

    return c;

}

Copy the code

Significance of parental delegation model:

  • System classes prevent multiple copies of the same bytecode from appearing in memory
  • Ensure the safe and stable running of Java programs

Custom loader

Custom classloaders are generally inherited from classloaders, so we just need to override the findClass method. Here’s an example to illustrate the process of customizing a class loader:

package com.github.hcsp.classloader;



import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;



public class MyClassLoader extends ClassLoader {

    // Directory where the bytecode files are stored

    private final File bytecodeFileDirectory;



    public MyClassLoader(File bytecodeFileDirectory) {

        this.bytecodeFileDirectory = bytecodeFileDirectory;

    }



    // Remember what class loaders do?

    // "Load a Class definition (Class object) from an external system"

    // Implement a custom Class loader that loads the bytecode files in the current directory as Class objects

    // Note that, in general, to implement a custom classloader, you need to override the following methods:

    //

    // 1. If the bytecode file corresponding to the class name exists, it is read as a byte array

    // 1.1 Calls classLoader.defineclass () to convert the byte array to a Class object

    // 2. If the bytecode file corresponding to the class name does not exist, throw ClassNotFoundException

    //

    @Override

    protectedClass<? > findClass(String name)throws ClassNotFoundException {

        byte[] classData = getByteArrayFromFile(name);

        if (classData == null) {

            throw new ClassNotFoundException();

        }

        return defineClass(name, classData, 0, classData.length);

    }



    byte[] getByteArrayFromFile(String className) throws ClassNotFoundException {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        File file = new File(bytecodeFileDirectory, className + ".class");

        int len = 0;

        try {

            byte[] bufferSize = new byte[1024];

            FileInputStream fis = new FileInputStream(file);

            while((len = fis.read(bufferSize)) ! = -1) {

                bos.write(bufferSize, 0, len);

            }

        } catch (FileNotFoundException e) {

            throw new ClassNotFoundException();

        } catch (IOException e) {

            e.printStackTrace();

        }

        return bos.toByteArray();

    }



    public static void main(String[] args) throws Exception {

        File projectRoot = new File(System.getProperty("basedir", System.getProperty("user.dir")));

        MyClassLoader myClassLoader = new MyClassLoader(projectRoot);



        Class testClass = myClassLoader.loadClass("com.github.hcsp.MyTestClass");

        Object testClassInstance = testClass.getConstructor().newInstance();

        String message = (String) testClass.getMethod("sayHello").invoke(testClassInstance);

        System.out.println(message);

    }

}

Copy the code

The core of the custom class loader is the retrieval of the bytecode file, which needs to be decrypted in the class if it is encrypted bytecode. Since this is just a demonstration, I have not encrypted the class file, so there is no decryption.

other

Use reflection to create objects

You can instantiate this using the newInstance() method of Class

Class c = Class.forName(className);

Object obj = c.newInstance();

Copy the code

A method is called

In the class using reflection can retrieve Method, the corresponding class for Java. Lang. Reflect. The Method, you can use it to invoke () Method to invoke the specified Method

Class testClass = myClassLoader.loadClass("com.github.hcsp.MyTestClass");

Object testClassInstance = testClass.getConstructor().newInstance();

String message = (String) testClass.getMethod("sayHello").invoke(testClassInstance);

Copy the code