12000 + word Java reflection, to fully understand the Java reflection mechanism together and pave the way for the learning framework

Posted by BluntedbyNature on Sun, 03 Oct 2021 20:14:20 +0200

Java reflection mechanism

Reflection is the key to being regarded as a dynamic language. Reflection mechanism allows programs to obtain the internal information of any class with the help of Reflection API during execution, and can directly operate the internal properties and methods of any object.

After loading the Class, a Class object is generated in the method area of heap memory (a Class has only one Class object), which contains the complete Class structure information. We can see the structure of the Class through this object. This object is like a mirror, through which we can see the structure of the Class. Therefore, we vividly call it reflection.

After the program passes the javac.exe command, it will generate one or more bytecode files (. End of class). Then we use the java.exe command to interpret and run a bytecode file. Equivalent to a bytecode file
Load into memory. This process is called Class loading. The Class loaded into memory is called the runtime Class. This runtime Class is used as an instance of Class. In other words, an instance of Class corresponds to a runtime Class.


Functions provided by Java reflection mechanism:

Main API s related to reflection:

Understand Class

The following methods are defined in the Object class, which will be inherited by all subclasses:

public final Class getClass()

The type of the return value of the above method is a Class class, which is the source of Java reflection. In fact, the so-called reflection is also well understood from the running results of the program, that is, the Class can be obtained through object reflection.

The information that can be obtained after the object looks in the mirror: the properties, methods and constructors of a Class, and which interfaces a Class implements. For each Class, the JRE keeps an object of the same Class type for it. A Class object contains information about a specific structure (class/interface/enum/annotation/primitivetype/void / []).
① Class itself is also a class.
② Class objects can only be created by the system.
③ A loaded Class will only have one Class instance in the JVM.
④ A class object corresponds to a. Class file loaded into the JVM.
⑤ All loaded structures in a Class can be completely obtained through Class.
⑥ Class is the root of Reflection. For any class that wants to be dynamically loaded and run, you must first obtain the corresponding class object.

Get Class instance

Runtime classes loaded into memory will be cached for a certain period of time. Within this time, we can get this runtime class in different ways.

import com.atyeman.java.Person;

/**
 * @Author: Yeman
 * @Date: 2021-09-29-22:39
 * @Description:
 */
public class ClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //Method 1: call the attribute of the runtime class:. Class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //Method 2: call getClass() through the object of the runtime class
        Person p2 = new Person();
        Class clazz2 = p2.getClass();
        System.out.println(clazz2);
        //Method 3: call the static method of Class: forName(String,classPath)
        Class clazz3 = Class.forName("com.atyeman.java.Person");
        System.out.println(clazz3);
        //Method 4: use ClassLoader
        ClassLoader classLoader = ClassTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.atyeman.java.Person");
        System.out.println(clazz4);

        //By comparison, true is the same runtime class, which proves that only one is loaded
        System.out.println(clazz1 == clazz2 && clazz2 == clazz3 && clazz3==clazz4);
    }
}


What types can have Class objects?
① Class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
② interface: interface
③ []: array
④ enum: Enumeration
⑤ Annotation: annotation @ interface
⑥ primitive type: basic data type
⑦void

Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// As long as the element type is the same as the dimension, it is the same Class
System.out.println(c10 == c11); //true

Class loading process

When a program actively uses a class, if the class has not been loaded into memory, the system will initialize the class through the following three steps.

Load: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a java.lang.Class object representing this class as the access entry (i.e. reference address) of the class data in the method area. All class data that needs to be accessed and used can only be accessed through this class object. This loading process requires the participation of the class loader.

Link: the process of merging the binary code of Java classes into the running state of the JVM.

Verification: ensure that the loaded class information complies with the JVM specification. For example, starting with cafe, there is no security problem.

Preparation: the stage of formally allocating memory for class variables (static) and setting the default initial value of class variables. These memory will be allocated in the method area.

Resolution: the process of replacing the symbolic reference (constant name) in the virtual machine constant pool with a direct reference (address).

Initialization: the process of executing the < clinit > () method of the class constructor. The class constructor < clinit > () method is generated by the combination of the assignment action of all class variables in the class automatically collected at compile time and the statements in the static code block (the class constructor constructs class information, not the constructor of this class object). When initializing a class, if it is found that its parent class has not been initialized, the initialization of its parent class needs to be triggered first. Virtual opportunity ensures that the < clinit > () methods of a class are locked and synchronized correctly in a multithreaded environment.

public class ClassLoadingTest {
	public static void main(String[] args) {
		System.out.println(A.m);
	}
}
class A {
	static {
		m = 300;
	}
	static int m = 100;
}
//Step 2: m=0 after link
//Step 3: after initialization, the value of m is determined by the < clinit > () method
// The class constructor < clinit > () method of A is generated by combining the assignment of class variables and the statements in the static code block in order, which is equivalent to
// <clinit>(){
// m = 300;
// m = 100;
// }

When does class initialization occur?
1. Active reference of class (class initialization must occur)
① When the virtual machine starts, initialize the class where the main method is located first
② new is an object of a class
③ Call static members (except final constants) and static methods of the class
④ Use the methods of the java.lang.reflect package to make reflection calls to the class
⑤ When initializing a class, if its parent class is not initialized, its parent class will be initialized first
2. Passive reference of class (class initialization will not occur)
① When accessing a static domain, only the class that actually declares the domain will be initialized
② When a static variable of a parent class is referenced through a subclass, subclass initialization is not caused
③ Defining a class reference through an array does not trigger the initialization of this class
④ Reference constants do not trigger the initialization of this class (constants are stored in the constant pool of the calling class in the link phase)

ClassLoader


Functions of class loader:
① Class loading: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a java.lang.Class object representing this class in the heap as the access entry to the class data in the method area.
② Class caching: the standard JavaSe class loader can find classes as required, but once a class is loaded into the class loader, it will remain loaded (cached) for a period of time. However, the JVM garbage collection mechanism can recycle these class objects.

Class loader is used to load classes into memory. The JVM specification defines loaders for classes of the following types.

/**
 * @Author: Yeman
 * @Date: 2021-09-29-22:39
 * @Description:
 */
public class ClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //1. Get a system class loader
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //2. Get the parent class loader of the system class loader, that is, the extension class loader
        ClassLoader parent1 = systemClassLoader.getParent();
        System.out.println(parent1);
        //3. Get the parent class loader of the extension class loader, that is, the boot class loader
        ClassLoader parent2 = parent1.getParent();
        System.out.println(parent2);
        //4. Test which class loader loads the current class
        Class<?> aClass = Class.forName("com.atyeman.java.Person");
        ClassLoader classLoader = aClass.getClassLoader();
        System.out.println(classLoader);
        //5. Test which class loader loads the Object class provided by JDK
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1); //null indicates that it is a boot class loader and cannot be obtained
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @Author: Yeman
 * @Date: 2021-09-29-22:39
 * @Description:
 */
public class ClassTest {
    public static void main(String[] args) throws IOException {
        Properties pros = new Properties();

        //Read configuration file mode 1
//        FileInputStream fis = new FileInputStream("ClassTest\\jdbc.properties");
//        pros.load(fis);

        //The second way to read the configuration file is to use ClassLoader
        ClassLoader classLoader = ClassTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println(user + password);

    }
}

Create an object for the runtime class

public class NewInstanceTest {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Class<Person> pc = Person.class;
        Person person = pc.newInstance();
        System.out.println(person);
    }
}

.
Call the newInstance () method to create the object of the corresponding runtime class. Constructor that internally calls an empty parameter of the runtime class.

Call newInstance() method to create the object of runtime class normally. Requirements:
① The runtime class must provide a constructor with null parameters
② The access rights of constructors with null parameters are usually public

A public null parameter constructor is required in the javabean. reason:
① An object that creates a runtime class through reflection
② When a subclass inherits this runtime class, it is guaranteed that the parent class has this constructor when super() is called by default

Gets the structure of the runtime class

1. Get attribute structure
public Field[] getFields(): attributes declared as public in this Class, its parent Class and interface
Public field [] getdeclaraedfields(): all attributes declared in this Class
In the Field method:
public int getModifiers(): returns the modifier of this Field as an integer, which can be restored using Modifier.toString(int modifier)
public Class<?> GetType (): get the attribute type of Field
public String getName(): returns the name of the Field

2. Get method structure
public Method[] getMethods(): the method declared as public in this Class, its parent Class and interface
Public method [] getdeclaraedmethods(): all methods declared in this Class
Method class:
public Class<?> Getreturntype(): get all return values
public Class<?> [] getparametertypes(): get all parameters
public int getModifiers(): get modifiers
public Class<?> [] getexceptiontypes(): get exception information

3. Get constructor
Public constructor < T > [] getconstructors(): constructor declared as public in this Class
Public constructor < T > [] getdeclaraedconstructors(): all constructors in this Class
In the Constructor class:
Get modifier: public int getModifiers()
Get method name: public String getName()
Get the type of parameter: public class <? > [] getParameterTypes()

4. Other
public Class<?> [] getinterfaces(): all interfaces implemented by this class
public Class<? Super T > getsuperclass(): the parent class of this class
Type getGenericSuperclass(): the parent Class of this Class with generics
Get annotation (class < T > annotation class): the annotation of this structure
ParameterizedType: generic type of this Class
Package getPackage(): the package of this Class

Calls the specified structure of the runtime class

1. Call the specified property
In the reflection mechanism, you can directly operate the properties in the class through the Field class, and use the set() and
The get() method can complete the operation of setting and obtaining the property content.
public Field getField(String name): returns the specified public Field of the Class or interface represented by this Class object. (not common, not commonly used)
Public Field getdeclaraedfield (string name): returns the specified Field of the Class or interface represented by this Class object.
In Field:
public Object get(Object obj): get the attribute content of this Field on the specified object obj
public void set(Object obj,Object value): sets the attribute content of this Field on the specified object obj

import java.lang.reflect.Field;
/**
 * @Author: Yeman
 * @Date: 2021-10-02-22:18
 * @Description:
 */
public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();
        //Gets the specified property
        Field name = personClass.getDeclaredField("name"); //Gets the specified property
        name.setAccessible(true); //For non public, you need to do this first to ensure that the current property can be accessed
        name.set(person,"chloroplast"); //Set which object the first parameter is and which value the second parameter is
        String pName = (String) name.get(person); //Gets which object the parameter is
        System.out.println(pName);
    }
}

2. Call the specified method
Through reflection, the Method in the class is called and completed through the Method class. Steps:
① Get a Method object through the getMethod(String name,Class... parameterTypes) Method of Class class, and set the parameter type required for this Method operation.
② Use Object invoke(Object obj, Object[] args) to call and pass the parameter information of the obj object to be set to the method.

import java.lang.reflect.Method;

/**
 * @Author: Yeman
 * @Date: 2021-10-02-22:18
 * @Description:
 */
public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();
        //Gets the specified method. The first parameter is which method, and the subsequent parameter is the formal parameter list
        Method show = personClass.getDeclaredMethod("show", String.class);
        //Settings can be accessed
        show.setAccessible(true);
        //Use the invoke() method for specific operations, and the return value is consistent with the original method
        //Which object is the first parameter, and the following parameters are the passed arguments
        String nation = (String) show.invoke(person, "China");
        System.out.println(nation);
    }
}

3. Call the specified constructor (not commonly used)
Similar to properties and methods.

Dynamic agent

Wrap the object with a proxy object and replace the original object with the proxy object. Any call to the original object is made through a proxy. The proxy object determines whether and when method calls are transferred to the original object. The static proxy feature is that the proxy class and the class of the target object are determined during compilation, which is not conducive to the expansion of the program. At the same time, each agent class can only serve one interface, so there must be too many agents in program development. It is best to complete all proxy functions through a proxy class.

Dynamic proxy refers to the method that the client calls other objects through the proxy class, and it is the proxy object that dynamically creates the target class when the program runs.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author: Yeman
 * @Date: 2021-10-03-19:33
 * @Description:
 */

interface Human {
    String getBelief();
    void eat(String food);
}

//A proxy class
class superMan implements Human {
    @Override
    public String getBelief() {
        return "I believe the light!";
    }

    @Override
    public void eat(String food) {
        System.out.println("eat" + food);
    }
}

//Dynamic proxy class
class ProxyFactory {
    //Call this method to return an object of a proxy class
    public static Object getProxyInstance(Object obj){ //obj is the proxied class
        MyInvocationHandle handle = new MyInvocationHandle();
        handle.bind(obj); //Will be notified by the proxy class
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handle);
    }
}
//It is used to call the same name and parameter method in the corresponding proxy class object when calling the method of the proxy class object
class MyInvocationHandle implements InvocationHandler {
    private Object obj; //Object of the proxied class

    public void bind(Object obj){
        this.obj = obj;
    }
    //When the object of the proxy class calls method a, the following methods will be called automatically
    //Declare the function of method a to be executed by the proxy class in invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Method: refers to the method called by the proxy class object, which is also used as the method called by the proxy class object
        //obj: object of the proxied class
        return method.invoke(obj,args);
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        superMan superMan = new superMan();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //Proxy class object
        String belief = proxyInstance.getBelief(); //When a contemporary class object calls a method, it calls the same name and parameter method in the proxy
        System.out.println(belief);
        proxyInstance.eat("Hot Pot");
    }
}

Proxy: it is a special operation class for proxy. It is the parent class of all dynamic proxy classes. This class dynamically generates implementation classes for one or more interfaces.

Provides static methods for creating dynamic proxy classes and dynamic proxy objects:

Dynamic proxy steps:




Topics: Java Class reflection Dynamic Proxy