Basic understanding and use of reflection

Reflection is the soul of frame design

1. Overview of reflection

The JAVA reflection mechanism is that in the running state, for any class, you can know all the properties and methods of this class; for any object, you can call any of its methods and properties; this dynamically obtained information and dynamic calls The function of the object’s method is called the reflection mechanism of the Java language.
To dissect a class, you must first obtain the bytecode file object of the class. The dissection uses the methods in the Class class. Therefore, you must first obtain the Class type object corresponding to each bytecode file.

Summary: Reflection is to map various components in a Java class into Java objects.

For example: a class has: member variables, methods, constructors, packages and other information. Reflection technology can be used to dissect a class and map each component into an object. (Actually: these member methods, construction methods in a class, and the added class have a class to describe them)

The picture shows the normal loading process of a class: the principle of reflection lies in the Class object. Familiarize yourself with loading: The origin of theClass object is to read the class file into memory and create a Class object for it.

2. The use of reflection (the Student class is used for demonstration here)

1. Three ways to obtain Class objects

1.1 Object –> getClass();
1.2 Any data type (including basic data types) has a “static” class attribute
1.3 Through the static method of Class class: forName (String className) (commonly used)

Among them, 1.1 is because of the getClass method in the Object class, because all classes inherit the Object class. Thus calling the Object class to obtain

/**
 * Three ways to obtain Class objects
 * 1 Object --> getClass();
 * 2 Any data type (including basic data types) has a "static" class attribute
 * 3 Through the static method of Class class: forName (String className) (commonly used)
 *
 */
public class Fanshe {
    public static void main(String[] args) {
        //The first way to get the Class object
        Student stu1 = new Student();//This new generates a Student object and a Class object.
        Class stuClass = stu1.getClass();//Get Class object
        System.out.println(stuClass.getName());

        //Second way to get Class object
        Class stuClass2 = Student.class;
        System.out.println(stuClass == stuClass2);//Determine whether the Class object obtained in the first way and the Class object obtained in the second way are the same

        //The third way to obtain the Class object
        try {
            Class stuClass3 = Class.forName("fanshe.Student");//Note that this string must be a real path, which is the class path with the package name, package name.class name
            System.out.println(stuClass3 == stuClass2);//Determine whether the three methods obtain the same Class object
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

Note: During runtime, only one Class object is generated for a class.

The third method is commonly used. If the first type of object is available, why should reflection be used? The second type of package requires importing classes. The dependency is too strong. If the package is not imported, a compilation error will be thrown. Generally the third method is used. A string can be passed in or written in the configuration file in various ways.

2. Obtain the construction method through reflection and use:

Student class

public class Student {

    //---------------Construction method-------------------  
    //(default constructor)
    Student(String str){
        System.out.println("(default) constructor s = " + str);
    }

    //No parameter constructor
    publicStudent(){
        System.out.println("The public, parameterless constructor was called and executed...");
    }

    //Constructor with one parameter
    public Student(char name){
        System.out.println("Name: " + name);
    }

    //Constructor with multiple parameters
    public Student(String name,int age){
        System.out.println("Name:" + name + "Age:" + age);//There is a problem with the execution efficiency of this, which will be solved later.
    }

    //Protected constructor
    protected Student(boolean n){
        System.out.println("Protected constructor n = " + n);
    }

    //private constructor
    private Student(int age){
        System.out.println("Private constructor age: " + age);
    }

} 

There are 6 construction methods in total;
Test class:

import java.lang.reflect.Constructor;


/*
 * Through the Class object, you can obtain: constructors, member variables, and member methods in a certain class; and access members;
 *
 * 1. Get the construction method:
 *1).Batch method:
 * public Constructor[] getConstructors(): all "public" constructors
            public Constructor[] getDeclaredConstructors(): Get all constructors (including private, protected, default, public)

 * 2). Get a single method and call:
 * public Constructor getConstructor(Class... parameterTypes): Get a single "public" constructor:
 * public Constructor getDeclaredConstructor(Class... parameterTypes): Get "a certain constructor" which can be private, protected, default, or public;
 *
 * Call the constructor:
 * Constructor-->newInstance(Object... initargs)
 */
public class Constructors {

    public static void main(String[] args) throws Exception {
        //1.Load Class object
        Class clazz = Class.forName("fanshe.Student");


        //2. Get all public constructors
        System.out.println("************************All public constructors********************** ****************");
        Constructor[] conArray = clazz.getConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }


        System.out.println("************All constructors (including: private, protected, default, public)****************** ");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("******************Get the public, no-parameter construction method****************** *************");
        Constructor con = clazz.getConstructor(null);
        //1>, because it is a parameterless constructor, the type is null, you can not write it: what is needed here is the type of a parameter, remember to be the type
        //2>. What is returned is the class object describing this parameterless constructor.

        System.out.println("con = " + con);
        //Call the constructor
        Object obj = con.newInstance();
    // System.out.println("obj = " + obj);
    // Student stu = (Student)obj;

        System.out.println("******************Get the private constructor and call ****************** *************");
        con = clazz.getDeclaredConstructor(char.class);
        System.out.println(con);
        //Call the constructor
        con.setAccessible(true);//Violent access (ignoring the access modifier)
        obj = con.newInstance('male');
    }

} 

Background output:

 ************************All public constructors************************ ************
    public fanshe.Student(java.lang.String,int)
    public fanshe.Student(char)
    public fanshe.Student()
    ************All construction methods (including: private, protected, default, public)******************
    private fanshe.Student(int)
    protected fanshe.Student(boolean)
    public fanshe.Student(java.lang.String,int)
    public fanshe.Student(char)
    public fanshe.Student()
    fanshe.Student(java.lang.String)
    *****************Get the public, no-parameter construction method************************ ****
    con = public fanshe.Student()
    The public, no-argument constructor is called and executed. . .
    ******************Get the private constructor and call ************************ ****
    public fanshe.Student(char)
    Name: Male 

Calling method:

1. Get the construction method:

  • public Constructor[] getConstructors(): all “public” constructors

  • public Constructor[] getDeclaredConstructors(): Get all constructors (including private, protected, default, public)

1.1. Get a single method and call:

  • public Constructor getConstructor(Class… parameterTypes): Get a single “public” constructor:

  • public Constructor getDeclaredConstructor(Class… parameterTypes): Get “a certain constructor” which can be private, protected, default, or public;

Call constructor:

  • Constructor–>newInstance(Object… initargs)

2. newInstance is a method of the Constructor class (a class that manages constructors)
The explanation of api is: newInstance(Object… initargs)

Use the constructor represented by this Constructor object to create a new instance of the constructor’s declaring class and initialize the instance with the specified initialization parameters.

Its return value is of type T, so newInstance is a new instance object of the declaring class that creates a constructor method. and call it

3. Get member variables and call them

student class:

public class Student {
    publicStudent(){
    }
    //************Field******************//
    public String name;
    protected int age;
    char sex;
    private String phoneNum;
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex
                 + ", phoneNum=" + phoneNum + "]";
    }
}

Test class:

import java.lang.reflect.Field;
/*
 * Get member variables and call:
 *
 * 1. Batch
 * 1).Field[] getFields(): Get all "public fields"
 * 2).Field[] getDeclaredFields(): Get all fields, including: private, protected, default, public;
 * 2. Get a single:
 * 1).public Field getField(String fieldName): Get a "public" field;
 * 2).public Field getDeclaredField(String fieldName): Get a certain field (can be private)
 *
 * Set the value of the field:
 * Field --> public void set(Object obj,Object value):
 *                  Parameter Description: 
 * 1.obj: The object where the field to be set is located;
 * 2.value: The value to be set for the field;
 *
 */
public class Fields {

        public static void main(String[] args) throws Exception {
            //1. Get the Class object
            Class stuClass = Class.forName("fanshe.field.Student");
            //2. Get fields
            System.out.println("************Get all public fields**********************");
            Field[] fieldArray = stuClass.getFields();
            for(Field f : fieldArray){
                System.out.println(f);
            }
            System.out.println("************Get all fields (including private, protected, default)********************** ***");
            fieldArray = stuClass.getDeclaredFields();
            for(Field f : fieldArray){
                System.out.println(f);
            }
            System.out.println("******************Get public fields** and call****************************** ************");
            Field f = stuClass.getField("name");
            System.out.println(f);
            //Get an object
            Object obj = stuClass.getConstructor().newInstance();//Generate Student object--》Student stu = new Student();
            //Set value for field
            f.set(obj, "Andy Lau");//Assign a value to the name attribute in the Student object--"stu.name = "Andy Lau"
            //verify  
            Student stu = (Student)obj;
            System.out.println("Verification name: " + stu.name);


            System.out.println("**************Get private fields**** and call******************** ************");
            f = stuClass.getDeclaredField("phoneNum");
            System.out.println(f);
            f.setAccessible(true);//Violent reflection, lifting private restrictions
            f.set(obj, "18888889999");
            System.out.println("Verification phone number: " + stu);

        }
}

Background output:

************Get all public fields************************
public java.lang.String fanshe.field.Student.name
************Get all fields (including private, protected, default)************************
public java.lang.String fanshe.field.Student.name
protected int fanshe.field.Student.age
char fanshe.field.Student.sex
private java.lang.String fanshe.field.Student.phoneNum
*************Get the public fields** and call****************************** *****
public java.lang.String fanshe.field.Student.name
Verified name: Andy Lau
******************Get private fields**** and call *************************** *****
private java.lang.String fanshe.field.Student.phoneNum
Verification phone number: Student [name=Andy Lau, age=0, sex= 

It can be seen that

When calling a field: Two parameters need to be passed:

  • Object obj = stuClass.getConstructor().newInstance();//Generate Student object

  • Student stu = new Student();

//Set value for field

f.set(obj, “Andy Lau”);//Assign a value to the name attribute in the Student object–》stu.name = “Andy Lau”
The first parameter: the setting object to be passed in, the second parameter: the actual parameters to be passed in

4. Get member methods and call them

student class:

public class Student {
    //**************Member method******************//
    public void show1(String s){
        System.out.println("Called: public, String parameter show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("Called: protected, parameterless show2()");
    }
    void show3(){
        System.out.println("Called: default, no-parameter show3()");
    }
    private String show4(int age){
        System.out.println("Called, private, and has a return value, show4() with int parameter: age = " + age);
        return "abcd";
    }
} 

Test class:

import java.lang.reflect.Method;
/*
 * Get the member method and call:
 *
 * 1. Batch:
 * public Method[] getMethods(): Get all "public methods"; (including the methods of the parent class and the Object class)
 * public Method[] getDeclaredMethods(): Get all member methods, including private ones (excluding inherited ones)
 * 2. Get a single:
 * public Method getMethod(String name,Class<?>... parameterTypes):
 * Parameters:
 * name: method name;
 * Class...: Class type object of formal parameter
 * public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 *
 * Calling method:
 * Method --> public Object invoke(Object obj,Object... args):
 *                  Parameter Description: 
 * obj: the object on which the method is to be called;
 * args: the actual parameters passed when calling the method;
 */
public class MethodClass {
    public static void main(String[] args) throws Exception {
        //1. Get the Class object
        Class stuClass = Class.forName("fanshe.method.Student");
        //2. Get all public methods
        System.out.println("******************Get all "public" methods**********************") ;
        stuClass.getMethods();
        Method[] methodArray = stuClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("******************Get all methods, including private *******************" );
        methodArray = stuClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("******************Get the public show1() method**********************") ;
        Method m = stuClass.getMethod("show1", String.class);
        System.out.println(m);
        //Instantiate a Student object
        Object obj = stuClass.getConstructor().newInstance();
        m.invoke(obj, "Andy Lau");
        System.out.println("******************Get private show4() method******************");
        m = stuClass.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//Remove private restrictions
        Object result = m.invoke(obj, 20);//Requires two parameters, one is the object to be called (obtained with reflection), and the other is the actual parameter
        System.out.println("Return value: " + result);
    }
} 

Console output:

******************Get all "public" methods**********************
public void fanshe.method.Student.show1(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************Get all methods, including private ones******************
public void fanshe.method.Student.show1(java.lang.String)
private java.lang.String fanshe.method.Student.show4(int)
protected void fanshe.method.Student.show2()
void fanshe.method.Student.show3()
***************Get the public show1() method************************
public void fanshe.method.Student.show1(java.lang.String)
Called: public, String parameter show1(): s = Andy Lau
***************Get the private show4() method******************
private java.lang.String fanshe.method.Student.show4(int)
Called, private, and has a return value, show4() with int parameter: age = 20
Return value: abcd 

It can be seen that:

m = stuClass.getDeclaredMethod(“show4”, int.class); //To call a specified method (all including private ones), two parameters need to be passed in. The first is the name of the method to be called, and the second is the form of the method. Parameter type, remember to be the type.

System.out.println(m);
m.setAccessible(true);//Remove private restrictions
Object result = m.invoke(obj, 20);//Requires two parameters, one is the object to be called (obtained with reflection), and the other is the actual parameter System.out.println(“Return value: ” + result); //

5. Reflection main method

student class:

public class Student {

    public static void main(String[] args) {
        System.out.println("main method executed...");
    }
} 

Test class:

import java.lang.reflect.Method;

/**
 * Obtain the main method of the Student class, do not confuse it with the current main method
 */
public class Main {

    public static void main(String[] args) {
        try {
            //1. Get the bytecode of the Student object
            Class clazz = Class.forName("fanshe.main.Student");

            //2. Get the main method
             Method methodMain = clazz.getMethod("main", String[].class);//The first parameter: the method name, the second parameter: the type of the method parameter,
            //3. Call main method
            // methodMain.invoke(null, new String[]{"a","b","c"});
             //The first parameter is the object type. Because the method is static, it can be null. The second parameter is a String array. It should be noted here that it is an array in jdk1.4 and is a variable parameter after jdk1.5.
             //When splitting here, split new String[]{"a","b","c"} into 3 objects. . . So it needs to be forced.
             methodMain.invoke(null, (Object)new String[]{"a","b","c"});//Method 1
            // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//Method 2

        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

Console output:

The main method is executed. . .

6. Run the configuration file content through reflection

student class:

public class Student {
    public void show(){
        System.out.println("is show()");
    }
} 

The configuration file takes a txt file as an example (pro.txt):

className = cn.fanshe.Student
methodName = show 

Test class:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

/*
 * We use reflection and configuration files to enable: When the application is updated, there is no need to make any modifications to the source code.
 * We only need to send the new class to the client and modify the configuration file
 */
public class Demo {
    public static void main(String[] args) throws Exception {
        //Get the Class object through reflection
        Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
        //2 Get the show() method
        Method m = stuClass.getMethod(getValue("methodName"));//show
        //3. Call the show() method
        m.invoke(stuClass.getConstructor().newInstance());

    }

    //This method receives a key and obtains the corresponding value in the configuration file.
    public static String getValue(String key) throws IOException{
        Properties pro = new Properties();//Get the object of the configuration file
        FileReader in = new FileReader("pro.txt");//Get the input stream
        pro.load(in);//Load the stream into the configuration file object
        in.close();
        return pro.getProperty(key);//Return the value obtained based on key
    }
} 

Console output:

is show()

need:

When we upgrade this system, instead of the Student class, we need to write a new Student2 class. At this time, we only need to change the content of the pro.txt file. No need to change the code at all

Student2 class to replace:

public class Student2 {
    public void show2(){
        System.out.println("is show2()");
    }
} 

The configuration file is changed to:

className = cn.fanshe.Student2
methodName = show2

Console output:

is show2();

7. Overcoming generic checks through reflection

Generics are used at compile time and are erased (disappeared) after compilation. So it is possible to bypass the generic check through reflection.

Test class:

import java.lang.reflect.Method;
import java.util.ArrayList;

/*
 * Override generic checking through reflection
 *
 * For example: There is a collection of String generic type. How can I add a value of type Integer to this collection?
 */
public class Demo {
    public static void main(String[] args) throws Exception{
        ArrayList<String> strList = new ArrayList<>();
        strList.add("aaa");
        strList.add("bbb");

        // strList.add(100);
        //Get the Class object of ArrayList, call the add() method in reverse, and add data
        Class listClass = strList.getClass(); //Get the bytecode object of the strList object
        //Get the add() method
        Method m = listClass.getMethod("add", Object.class);
        //Call the add() method
        m.invoke(strList, 100);

        //Traverse the collection
        for(Object obj : strList){
            System.out.println(obj);
        }
    }
} 

Console output:

aaa
bbb
100