JAVA advanced learning notes: Reflection

Posted by mitsubishi2002 on Thu, 27 Jan 2022 00:41:26 +0100

1. General

1.1 concept of reflection

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, an object of Class type is generated in the method area of heap memory (a Class has only one Class object), which contains the complete structure information of the Class. 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.

1.2 application

Determine the class of any object at run time
Construct an object of any class at run time
Judge the member variables and methods of any class at run time
Get generic information at run time
Call the member variables and methods of any object at run time
Processing annotations at run time
Generate dynamic proxy

1.3 examples of reflection

Person.java:

package com.wzc.java;

public class Person {
    private String name;
    public int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person() {
    }

    private Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void show(){
        System.out.println("Hello, I'm alone");
    }

    private String showNation(String nation){
        System.out.println("My nationality is" + nation);
        return nation;
    }

}
 //Operation on Person before reflection
    @Test
    public void test1(){
        //1. Create the object of Person class
        Person p1 = new Person("Tom",12);

        //2. Call the properties and methods through the object
        p1.age = 10;
        System.out.println(p1.toString());

        p1.show();
        //Outside the Person class, its internal private structure cannot be called through the object of the Person class
        //For example: name, showNation() and private constructor
    }

    //With reflection, the operation of Person
    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
        //1. Create an object of Person class through reflection
        Constructor cons = clazz.getConstructor(String.class, int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person)obj;
        System.out.println(p.toString());
        //2. Call the object to specify the attribute method through reflection
        //Call properties
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p.toString());
        //Call method
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);
        System.out.println("*************************************");

        //Reflection allows you to call the private structure of the Person class. For example, private constructors, methods, and properties
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person)cons1.newInstance("Jerry");
        System.out.println(p1);
        //Call private property
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"LaoLiu");
        System.out.println(p1);
        //Call private method
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation  = (String)showNation.invoke(p1,"China");//Equivalent to string nation = P1 Shownation ("China")
        System.out.println(nation);
    }

2. Understand the Class and get the instance of Class

2.1 understanding of class

1. Class loading process: the program passes javac Exe command will generate one or more bytecode files (. End of class). Then we use Java Exe command to interpret and run a bytecode file. It is equivalent to loading a bytecode file 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.
2. In other words, an instance of Class corresponds to a runtime Class.
3. Runtime classes loaded into memory will be cached for a certain time. Within this time, we can get this runtime class in different ways.

2.2 method of obtaining Class instance

   @Test
    public void test3() throws ClassNotFoundException {
        //Method 1: call the properties 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 p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);
        //Method 3: call the static method of Class: forname (striding classpath)
        Class clazz3 = Class.forName("com.wzc.java.Person");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2); //true points to the same address and corresponds to the same class
        System.out.println(clazz2 == clazz3); //true

        //Method 4: use class loader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.wzc.java.Person");
        System.out.println(clazz4);

        System.out.println(clazz4 == clazz1); //true
    }

There are some types of Class objects:
(1) Class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
(2) interface: interface
(3) []: array
(4) enum: Enumeration
(5) Annotation: annotation @ interface
(6) primitive type: basic data type
(7)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 array element type is the same as the dimension, it is the same Class
System.out.println(c10 == c11); //true

3. Class loading process and ClassLoader

3.1 loading process of class



Function 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. Net file representing this class in the heap Lang. class object, as the access entry of class data in the method area.
Class caching: the standard Java se 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.

3.2 ClassLoader understanding

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

Learn about class loaders:

    @Test
    public void test1(){
        //For custom classes, use the system class loader to load them
        ClassLoader classLoader = ClassLoadertest.class.getClassLoader();
        System.out.println(classLoader); //System loader
        //Call getParent() of the system class loader: get the extended class loader
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);//extensions class loader 
        //Calling getParent() of extender loader class: unable to get boot class loader
        //The boot class loader is mainly responsible for loading the core class library of java, and cannot load custom classes
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//null does not mean there is no, but the bootstrap class loader cannot be obtained

        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3); //null boot class loader
    }

3.3 loading files with loader

For example, load JDBC files (two methods):

    @Test
    public void test2() throws Exception {

        Properties pros = new Properties();
        //At this time, the file is under the current module by default
        //Method 1 for reading configuration files:
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
//        pros.load(fis);

        //The second way to read the configuration file is to use classloader
        //The default identification of the configuration file is: under src of the current module
        ClassLoader classLoader = ClassLoadertest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);

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

4. Create the object of the runtime class

4.1 get the object of runtime class through reflection

    @Test
    public void test1() throws IllegalAccessException, InstantiationException {
        Class<Person> clazz = Person.class;
        /*newInstance():Call this method to create the object of the corresponding runtime class. An empty argument constructor of the class was called internally
        To create an object of a runtime class using this method:
        1.The runtime must provide an empty argument constructor
        2.Empty argument constructor has sufficient access rights. Usually public

        A public null parameter constructor is required in the javabean
        1.It is convenient to create objects of runtime classes through reflection
        2.When a subclass inherits this running class, it is guaranteed that the parent class has this constructor when super() is called by default
         */
        Person obj = clazz.newInstance(); //This method is outdated after Java 9
        System.out.println(obj);
    }

4.2 experience the dynamics of reflection

java is a dynamic language. It can change its structure according to some conditions at runtime. For example, according to some judgment statements, the creation of objects can not always be the same. Reflection mechanism can create corresponding classes in this dynamic language.

    //Experience the dynamics of reflection
    @Test
    public void test2(){
        for (int i = 0; i < 100; i++) {
            int num = new Random().nextInt(3); // 0,1,2
            String classPath = "";
            switch (num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.wzc.java.Person";
                    break;
            }

            try {
                Object instance = getInstance(classPath);
                System.out.println(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    //Create an object of the specified class. classpath: Specifies the full class name of the class
    public Object getInstance(String classPath) throws Exception {
        Class Clazz = Class.forName(classPath);
        return Clazz.newInstance();
    }

5. Get the complete structure of the runtime class

5.1 get the attribute structure of the current class

    @Test
    public void test1(){
        Class clazz = Person.class;

        //Get attribute structure
        //getFields(): get the attributes declared as public access rights in the current runtime class and parent class
        Field[] fields = clazz.getFields();
        for (Field f : fields) {
            System.out.println(f);
        }

        System.out.println();

        //Getdeclaraedfields(): get all the properties declared in the current runtime class. (excluding properties declared in the parent class)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(f);
        }
    }

    @Test
    public void test3(){
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f:declaredFields){
            //1. Permission modifier
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + ",  ");
            //2. Data type
            Class type = f.getType();
            System.out.print(type.getName() + ",  ");
            //3. Variable name
            String fName = f.getName();
            System.out.print(fName);

            System.out.println();
        }
    }

5.2 get the method structure of the current class

   @Test
    public void test1(){
        Class clazz = Person.class;

        //getMethods(): get the methods declared as publi permission in the current runtime class and all its parent classes
        Method[] methods = clazz.getMethods();
        for(Method m:methods){
            System.out.println(m);
        }
        System.out.println();

        //Getdeclaraedmethods(): get all the methods declared in the current runtime class. (excluding those declared in the parent class)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m: declaredMethods){
            System.out.println(m);
        }
    }

    //@Xxxx
    //Permission modifier return value type method name (parameter type 1, parameter name 1...) throws XxxException()
    @Test
    public void test2(){

        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            //1. Get the annotation of the method declaration
            Annotation[] annos = m.getAnnotations();
            for(Annotation a : annos){
                System.out.print(a);
            }

            //2. Permission modifier
            System.out.print(Modifier.toString(m.getModifiers()) + ", ");

            //3. Return value type
            System.out.print(m.getReturnType().getName() + ", ");

            //4. Method name
            System.out.print(m.getName());

            //5. Formal parameter list
            System.out.print("(");
            Class[] parameterTypes = m.getParameterTypes();
            if(!(parameterTypes == null && parameterTypes.length == 0)){
                for(int i=0; i<parameterTypes.length; i++){
                    if(i==parameterTypes.length - 1){
                        System.out.print(parameterTypes[i].getName() + " agrs_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + " agrs_" + i + ",");
                }
            }
            System.out.print(")");

            //6. Exceptions thrown
            Class[] exceptionTypes = m.getExceptionTypes();
            if(exceptionTypes.length > 0){
                System.out.print("throws ");
                for (int i=0; i<exceptionTypes.length; i++){
                    if(i == exceptionTypes.length - 1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ", ");
                }
            }

            System.out.println();
        }
    }

5.3 get constructor of current class

  @Test
    public void test1(){
        Class clazz = Person.class;
        //getConstructors(): get the constructor declared as public in the current runtime class
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }
        System.out.println();

        //Getdeclaraedconstructors(): get all constructors declared in the current running class
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
            System.out.println(c);
        }
    }

5.4 get the parent class and generic type of the current class

   //Gets the parent class of the runtime
    @Test
    public void test2(){
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

    //Gets or runs the parent class with generics
    @Test
    public void test3(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

    //Gets the generic type that runs the parent class with the generic type
    @Test
    public void test4(){
        Class clazz  = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //Get generic type
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0]);
        System.out.println(((Class)actualTypeArguments[0]).getName());
    }

5.4 get the annotation of the interface, package and declaration of the current class

    //Gets the interface implemented by the runtime class
    @Test
    public void test5(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }
        System.out.println();

        //Gets the interface of the runtime parent class
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
            System.out.println(c);
        }
    }

    //Gets the package where the runtime class is located
    @Test
    public void test6(){
        Class clazz = Person.class;

        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
    }

    //Gets the annotation of the runtime class declaration
    @Test
    public void test7(){
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation annos : annotations){
            System.out.println(annos);
        }
    }

6. Get the specified structure of the runtime class

6.1 get the specified properties of the runtime class

    //No
    @Test
    public void testFiled() throws Exception {
        Class clazz = Person.class;

        //Create an object for the runtime class
        Person p = (Person) clazz.newInstance();
        //Get the specified property: the property in the runtime class is required to be declared public
        //This method is not usually used
        Field id = clazz.getField("id");

        //Sets the value of the current property
        //set(): parameter 1: indicates which object's property is set. Parameter 2: how much this property value is set
        id.set(p,1001);

        //Gets the value of the current property
        //get(): parameter 1: get the current property value of which object
        int pId = (int) id.get(p);
        System.out.println(pId);
    }

    //Yes
    //The specified property in the action runtime class
    @Test
    public void test2() throws Exception {
        Class clazz = Person.class;
        //Create an object for the runtime class
        Person p = (Person)clazz.newInstance();

        //1. Getdeclaraedfield (string fieldname): get the attribute of the specified variable name in the runtime class
        Field name = clazz.getDeclaredField("name");
        //2. Ensure that the current attribute is accessible
        name.setAccessible(true);
        //3. Get and set the property value of the specified object
        name.set(p, "Tom");
        System.out.println(name.get(p));
    }

6.2 get the specified method of the runtime class

   //Yes
    //The specified method in the operation runtime class
    @Test
    public void test3() throws Exception {
        Class clazz = Person.class;

        //Create runtime objects
        Person p = (Person)clazz.newInstance();

        //Gets a specified method
        //getDeclaredMethod(): parameter 1: indicates the name of the obtained method; parameter 2: indicates the formal parameter list of the obtained method
        Method show = clazz.getDeclaredMethod("show", String.class);
        //2. Ensure that the current method is accessible
        show.setAccessible(true);
        //3. invoke() of calling method: parameter 1: caller of method; parameter 2: argument assigned to method parameter
        //The return value of invoke() is the return value of the method called in the corresponding class.
        Object returnValue = show.invoke(p, "CHN");
        System.out.println(returnValue);

        System.out.println("***************How to call static methods**************");

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //Object returnVal = showDesc.invoke(null);  // The Person class has already been loaded
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);
    }

6.3 get the specified constructor of the runtime class

    //Calls the specified constructor in the runtime class
    @Test
    public void testConstructor() throws Exception{
        Class clazz = Person.class;

        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);

        Person per = (Person) constructor.newInstance("Tom");
        System.out.println(per);
    }

reference material:
[1] Shang Silicon Valley song Kanghong java basic course

Topics: Java reflection