15, Learning Java reflection

1. Understanding of reflection

**Reflection (reflection) * * is the key to being regarded as a dynamic language. The 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.

2. Experience the "dynamics" of reflection mechanism

  • Code example:
//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.atguigu.java.Person";
                break;
        }
        try {
            Object obj = getInstance(classPath);
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/*
Creates 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.getDeclaredConstructor().newInstance();
}

3. Functions provided by reflection mechanism

4. Understanding of class

  1. Class loading process:
Procedure process javac.exe After the command, one or more bytecode files are generated(.class ending). 

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 Class An example of.
  1. In other words, an instance of Class corresponds to a runtime Class.

  2. 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.

5. Several ways to obtain Class instances:

  • Code example: (the first three methods need to be mastered)
**//How to obtain an instance of Class (the first three methods need to be mastered)**
@Test
public void test3() throws ClassNotFoundException {
    //Method 1: call the attribute of the runtime class:. Class
    Class clazz1 = Person.class;
    System.out.println(clazz1);//class com.atguigu.java.Person
    //Method 2: call getClass() through the object of the runtime class
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    System.out.println(clazz2);//class com.atguigu.java.Person

    //Method 3: call the static method of Class: forName(String classPath)
    Class clazz3 = Class.forName("com.atguigu.java.Person");
    System.out.println(clazz3);//class com.atguigu.java.Person
//        clazz3 = Class.forName("java.lang.String");
//        System.out.println(clazz3);//class java.lang.String

    System.out.println(clazz1 == clazz2);//true
    System.out.println(clazz1 == clazz3);//true

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

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

}

6. Summary: how to create class objects?

Method 1: new + constructor

Method 2: to create an object of class Xxx, you can consider checking whether there are static methods in classes Xxx, Xxxs, XxxFactory and XxxBuilder. You can call its static method to create an Xxx object.

Mode 3: through reflection

Method 4: through deserialization

Method 5: object. clone()

7. Description of which structures class instances can be

Understanding ClassLoader  

1. Class loading process - understand

2. Function of class loader

**Function of 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 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. Classification of class loaders

Note: the name of the extension class loader before JDK 9 is Extension Classloader

After JDK 9, the extension class loader is renamed PlatformClassLoader

4. Java class compilation, running and execution process

5. Reflection application 1: create the object of runtime class

1. Code example

  • Code example:
@Test
public void test1() throws Exception{
    Class<Person> clazz = Person.class;

    Person obj = clazz.getDeclaredConstructor().newInstance();
    System.out.println(obj);//Person{name='null', age=0}
}

2. Description:

Before jdk 9, the method * * newinstance(), which is outdated after jdk 9, should be called using getdeclaraedconstructor(). Newinstance(): * * to create the object of the corresponding runtime class. Constructor that internally calls an empty parameter of the runtime class.

To create the object of the runtime class normally with this method, you need to:

1. The runtime class must provide a constructor with null parameters

2. Empty argument constructor has sufficient access rights. Normally, set to public. 

A public null parameter constructor is required in the javabean. reason:

1. It is convenient to create objects of runtime classes through reflection

2. When a subclass inherits this runtime class, it is called by default super()Ensure that the parent class has this constructor

6. Reflection application 2: get the complete structure of the runtime class

Through reflection, we can obtain all properties, methods, constructors, parent classes, interfaces, generics, packages, annotations, exceptions, etc. of the corresponding runtime class

Typical code:

  • Code example: get the properties of the runtime 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 its parent class
    Field[] fields = clazz.getFields();
    for (Field f : fields){
        System.out.println(f);//public int com.atguigu.java1.Person.id /  public double com.atguigu.java1.Creature.weight
    }

    //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);
    }
}
  • Code example: method to get runtime class
@Test
public void test1(){

    Class clazz = Person.class;

    //getMethods(): get the methods declared as pubilc 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();//Line feed
    //Getdeclaraedmethods(): get all the methods declared in the current runtime class( (does not contain methods declared in the parent class)
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method m : declaredMethods){
        System.out.println(m);
    }
}
  • Code example: get other structures of the runtime class: constructor, parent class, parent class with generics, generics of parent class with generics, implemented interface, package, declared annotation
/*
Get constructor structure
 */
@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);//public com.atguigu.java1.Person()
    }

    System.out.println();
    //Getdeclaraedconstructors(): get all constructors declared in the current runtime class
    Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    for (Constructor c : declaredConstructors) {
        System.out.println(c);
    }
}
/*
Gets the parent class of the runtime class
 */
@Test
public void test2() {
    Class clazz = Person.class;

    Class superclass = clazz.getSuperclass();
    System.out.println(superclass);//class com.atguigu.java1.Creature
}
/*
Gets the generic parent of the runtime class
 */
@Test
public void test3() {
    Class clazz = Person.class;

    Type genericSuperclass = clazz.getGenericSuperclass();
    System.out.println(genericSuperclass);//com.atguigu.java1.Creature<java.lang.String>
}
/*
Gets the generic type of the generic parent class of the runtime class

Code: logical code vs functional code
 */
@Test
public void test4() {
    Class clazz = Person.class;

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

/*
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 implemented by the runtime class parent class
    Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
    System.out.println(interfaces1[0]);//interface java.io.Serializable
}
/*
Gets the package where the runtime class is located
 */
@Test
public void test6(){
    Class clazz = Person.class;

    Package pack = clazz.getPackage();
    System.out.println(pack);//package com.atguigu.java1
}
/*
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);//@com.atguigu.java1.MyAnnotation("hi")
    }
}

7. Reflection application 3: call the specified structure of the runtime class  

1. Call the specified attribute:

  • Code example: how to operate the specified properties in the runtime class - you need to master
@Test
public void testField1() throws Exception{

    Class clazz = Person.class;

    //Create an object for the runtime class
    Person p = (Person) clazz.getDeclaredConstructor().newInstance();

    //1. Getdeclaraedfield (string fieldname): get the attribute of the specified variable name in the runtime class
    Field name = clazz.getDeclaredField("name");

    //2. setAccessible(true): 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));//Tom
}

2. Call the specified method:

  • Code example: how to operate the specified properties in the runtime class - you need to master
@Test
public void testMethod() throws Exception{

    Class clazz = Person.class;
    //Create an object for the runtime class
    Person p  = (Person) clazz.getDeclaredConstructor().newInstance();

    /*1. 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
    invoke()The return value is the return value of the method called in the corresponding class.
     */
    Object returnValue = show.invoke(p,"CHN");//My nationality is CHN
    System.out.println(returnValue);//CHN

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

    //private static void showDesc()
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    //This invoke() returns null if the method in the called runtime class does not return a value
    Object returnVal = showDesc.invoke(Person.class);//I am a lovely person
//        Object returnVal = showDesc.invoke(null);// I am a lovely person
    System.out.println(returnVal);//null

}

3. Call the specified constructor:

  • Code example: how to call the specified constructor in the runtime class
@Test
public void testConstructor() throws Exception{
    Class clazz = Person.class;

    //private Person(String name)
    /*
    1. Gets the specified constructor
    getDeclaredConstructor(): Parameters: indicates the list of parameters for the constructor
     */

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

    //2. Ensure that this constructor is accessible
    constructor.setAccessible(true);

    //3. Call this constructor to create the object of the runtime class
    Person per = (Person) constructor.newInstance("Tom");
    System.out.println(per);//Person{name='Tom', age=0, id=0}
}

8. Reflection application IV: dynamic agent

1. Principle of agent mode:

Wrap the object with a proxy object and replace the original object with the proxy object. Any original object is called through a proxy. The proxy object determines whether and when method calls are transferred to the original object.

2. Static proxy

2.1 examples:

Implement the methods in the Runnable interface to create multithreads.

Class MyThread implements Runnable{}//Equivalent to proxy class
Class Thread implements Runnable{}//Equivalent to proxy class
main(){
    MyThread t = new MyThread();

    Thread thread = new Thread(t);

    thread.start();//Start the thread; run() of calling thread
}

2.2 disadvantages of static agent:

① The classes of proxy class and target object are determined during compilation, which is not conducive to program expansion.

② Each agent class can only serve one interface, so there must be too many agents in program development.

3. Characteristics of dynamic agent:

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.

4. Implementation of dynamic agent

4.1 two main problems to be solved:

  • Question 1: how to dynamically create a proxy class and its object according to the proxy class loaded into memory( (implemented by Proxy.newProxyInstance())

  • Question 2: how to dynamically call the method with the same name in the proxy class when calling the method through the object of the proxy class( Through the implementation class of InvocationHandler interface and its method (invoke())

4.2 code implementation:

  • Code example:
class ProxyFactory{
//Call this method to return an object of a proxy class. Solve problem 1
public static Object getProxyInstance(Object obj){//obj: object of the proxied class
    MyInvocationHandler handler = new MyInvocationHandler();

    handler.bind(obj);

    return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}

}

class MyInvocationHandler implements InvocationHandler{

private Object obj;//You need to use the object of the proxy class for assignment

public void bind(Object obj){
    this.obj = obj;
}

//When we call method a through the object of the proxy class, we will automatically call the following method: invoke()
//The function of method a to be executed by the proxy class is declared in invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    HumanUtil util = new HumanUtil();
    util.method1();


    //Method: that is, the method called for the proxy class object, which is also the method to be called by the proxy class object
    //obj: object of the proxied class
    Object returnValue = method.invoke(obj,args);

    util.method2();

    //The return value of the above method is used as the return value of invoke() in the current class.
    return returnValue;
}
}

public class ProxyTest {
public static void main(String[] args) {
    SuperMan superMan = new SuperMan();
    //proxyInstance: object of proxy class
    Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
    //When a method is called through a proxy class object, it will automatically call the method with the same name in the proxy class
    String belief = proxyInstance.getBelief();
    System.out.println(belief);//I believe I can fly!
    proxyInstance.eat("Sichuan Mala");//I like Sichuan spicy hot

    System.out.println("*******************************");

    LiningClothFactory liningClothFactory = new LiningClothFactory();

    ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(liningClothFactory);

    proxyClothFactory.produceCloth();//Li Ning factory produces a batch of sportswear

}

Experience: the dynamics of reflection.

Tags: Java Algorithm

Posted on Thu, 02 Sep 2021 21:22:10 -0400 by zild1221