Re-learning Android Basics Series (3): Principles of Architectural Dynamic Programming Technology

foreword

This series of articles mainly summarizes the technical articles of the big guys. Android基础部分As a qualified Android development engineer, we must be proficient in java and android. Let’s talk about these in this issue~

[非商业用途,如有侵权,请告知我,我会删除]

DD: Android进阶开发各类文档/资料,也可关注公众号<Android苦做舟>获取。

1.Android高级开发工程师必备基础技能
2.Android性能优化核心知识笔记
3.Android+音视频进阶开发面试题冲刺合集
4.Android 音视频开发入门到实战学习手册
5.Android Framework精编内核解析
6.Flutter实战进阶技术手册
7.近百个Android录播视频+音视频视频dome
.......

Architecture dynamic programming technology principle

Dynamic programming core architecture: dynamic proxy

1 Introduction

Proxy mode is a design pattern that enables additional extension of the functionality of the source target without modifying the source target. That is, by accessing the proxy class of the source target, and then the proxy class accesses the source target. This way, the code of the source target does not need to be modified to extend the functionality. You only need to add it to the proxy class.

In fact, the core idea of ​​the proxy mode is so simple. In java, proxies are divided into static proxies and dynamic proxies. Among them, dynamic proxies are divided into interface-based dynamic proxies and subclass-based dynamic proxies according to different implementations.

Among them, the static proxy is relatively simple, so there is nothing to ask in the interview. In the proxy mode, the most frequently asked question is the dynamic proxy, and the dynamic proxy is also the core idea of ​​spring aop. Many other functions of spring are also realized through dynamic proxy, such as Interceptors, transaction control, etc.

Proficiency in dynamic proxy technology can make your business code more streamlined and elegant. If you need to write some middleware, dynamic proxy technology is an essential skill package.

Then this article will show you all the details of dynamic proxy.

2. Static proxy

Before talking about dynamic proxy, let's talk about static proxy first.

The so-called static proxy is to access the source object by declaring a clear proxy class.

We have 2 interfaces, Person and Animal. Each interface has two implementation classes, UML as shown below:

The code in each implementation class is almost the same, using Student as an example (other classes are almost exactly the same as this one)

public class Student implements Person{
    private String name;

    public Student() {
   }

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void wakeup() {
        System.out.println(StrUtil.format("学生[{}]早晨醒来啦",name));
    }

    @Override
    public void sleep() {
        System.out.println(StrUtil.format("学生[{}]晚上睡觉啦",name));
    }
}

wakeup()Suppose we want to do one thing now, which is to add a line of output before calling all implementation classes 早安~, and sleep()add a line of output before calling 晚安~. Then we only need to write 2 proxy classes PersonProxyand AnimalProxy:

PersonProxy:

public class PersonProxy implements Person {

    private Person person;

    public PersonProxy(Person person) {
        this.person = person;
    }

    @Override
    public void wakeup() {
        System.out.println("早安~");
        person.wakeup();
    }

    @Override
    public void sleep() {
        System.out.println("晚安~");
        person.sleep();
    }
}

AnimalProxy:

public class AnimalProxy implements Animal {

    private Animal animal;

    public AnimalProxy(Animal animal) {
        this.animal = animal;
    }

    @Override
    public void wakeup() {
        System.out.println("早安~");
        animal.wakeup();
    }

    @Override
    public void sleep() {
        System.out.println("晚安~");
        animal.sleep();
    }
}

The final execution code is:

public static void main(String[] args) {
    Person student = new Student("张三");
    PersonProxy studentProxy = new PersonProxy(student);
    studentProxy.wakeup();
    studentProxy.sleep();
    Person doctor = new Doctor("王教授");
    PersonProxy doctorProxy = new PersonProxy(doctor);
    doctorProxy.wakeup();
    doctorProxy.sleep();
    Animal dog = new Dog("旺旺");
    AnimalProxy dogProxy = new AnimalProxy(dog);
    dogProxy.wakeup();
    dogProxy.sleep();
    Animal cat = new Cat("咪咪");
    AnimalProxy catProxy = new AnimalProxy(cat);
    catProxy.wakeup();
    catProxy.sleep();
}

output:

早安~
学生[张三]早晨醒来啦
晚安~
学生[张三]晚上睡觉啦
早安~
医生[王教授]早晨醒来啦
晚安~
医生[王教授]晚上睡觉啦
早安~~
小狗[旺旺]早晨醒来啦
晚安~~
小狗[旺旺]晚上睡觉啦
早安~~
小猫[咪咪]早晨醒来啦
晚安~~
小猫[咪咪]晚上睡觉啦

in conclusion:

I believe there is no need to say more about the code of the static proxy, the code is very simple and easy to understand. Two proxy classes are used here, proxy Personand Animalinterface respectively.

Although this model is easy to understand, its shortcomings are also obvious:

  • There will be a large number of redundant proxy classes. Here are 2 interfaces. If there are 10 interfaces, 10 proxy classes must be defined.
  • Not easy to maintain, once the interface changes, both the proxy class and the target class need to be changed.

3. JDK dynamic proxy

Dynamic proxy, in layman's terms, is: instead of creating java proxy classes declaratively, a "virtual" proxy class is generated during operation and loaded by ClassLoader. This avoids the need to declare a large number of proxy classes like a static proxy.

JDK has supported the creation of dynamic proxy classes since version 1.3. There are only 2 main core classes: java.lang.reflect.Proxyand java.lang.reflect.InvocationHandler.

Still in the previous example, the code to implement the JDK dynamic proxy class is as follows:

Create a JdkProxy class for unified proxy:

public class JdkProxy implements InvocationHandler {
    private Object bean;
    public JdkProxy(Object bean) {
        this.bean = bean;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if (methodName.equals("wakeup")){
            System.out.println("早安~~~");
        }else if(methodName.equals("sleep")){
            System.out.println("晚安~~~");
        }
        return method.invoke(bean, args);
    }
}

Execution code:

public static void main(String[] args) {
    JdkProxy proxy = new JdkProxy(new Student("张三"));
    Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
    student.wakeup();
    student.sleep();
    proxy = new JdkProxy(new Doctor("王教授"));
    Person doctor = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
    doctor.wakeup();
    doctor.sleep();
    proxy = new JdkProxy(new Dog("旺旺"));
    Animal dog = (Animal) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Animal.class}, proxy);
    dog.wakeup();
    dog.sleep();
    proxy = new JdkProxy(new Cat("咪咪"));
    Animal cat = (Animal) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Animal.class}, proxy);
    cat.wakeup();
    cat.sleep();
}

explain:

It can be seen that, compared with the static proxy class, no matter how many interfaces there are, only one proxy class is needed here. The core code is also very simple. The only points to note are the following 2 points:

  • The JDK dynamic proxy needs to declare the interface. To create a dynamic proxy class, you must give this "virtual" class an interface. It can be seen that each bean created by the dynamic proxy class at this time is no longer the original object.

  • JdkProxyWhy do we still need to construct and pass in the original bean here ? Because after processing the additional functions, the method of the original bean needs to be executed to complete 代理the responsibilities.

    JdkProxyThe core method here is

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    

    Among them, proxy is the object after proxy (not the original object), method is the method to be proxied, and args is the parameter of the method.

    If you don't pass the original bean and use method.invoke(proxy, args)it directly, you will fall into an endless loop.

what can be represented

JDK's dynamic proxy is the most commonly used proxy method. Also called interface proxy. A few days ago, a friend asked me in the group whether dynamic proxy can proxy one class at a time, and whether multiple classes are allowed.

To put it bluntly, the JDK dynamic proxy only generates classes based on the interface "out of thin air". As for the specific execution, they are all proxied to the InvocationHandlerimplementation class. In the above example, I need to continue to execute the logic of the original bean before constructing the original bean. As long as you need, you can construct any object into this proxy implementation class. In other words, you can pass in multiple objects, or you don't proxy any classes. Just generate multiple proxy instances "out of thin air" for a certain interface, and these multiple proxy instances will eventually enter the InvocationHandlerimplementation class to execute a certain segment of common code.

Therefore, an actual scenario in previous projects is that I have multiple rule files defined in yaml, and a dynamic proxy class is generated for each yaml rule file by scanning the yaml file. To achieve this, I only need to define an interface in advance, and the defined InvocationHandlerimplementation class, and at the same time pass in the object parsed by yaml. In the end, these dynamic proxy classes will enter invokemethods to execute a common logic.

4. Cglib dynamic proxy

Spring's default dynamic proxy implementation before 5.X has always been jdk dynamic proxy. But starting from 5.X, spring began to use Cglib as a dynamic proxy implementation by default. And springboot has also turned to Cglib dynamic proxy implementation since 2.X.

What caused the spring system to switch to Cglib as a whole, and what are the disadvantages of jdk dynamic proxy?

So we are now going to talk about the dynamic proxy of Cglib.

Cglib is an open source project. Its bottom layer is the bytecode processing framework ASM. Cglib provides a more powerful dynamic proxy than jdk. The main advantages over jdk dynamic proxy are:

  • jdk dynamic proxy can only be based on the interface, the object generated by the proxy can only be assigned to the interface variable, and Cglib does not have this problem, Cglib is realized by generating subclasses, the proxy object can be assigned to the implementation class, and can also be assigned to the interface.
  • Cglib is faster and has better performance than jdk dynamic proxy.

So what is achieved through subclasses?

Still the previous example, we want to achieve the same effect. code show as below

Create the CglibProxy class for unified proxy:

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    private Object bean;
    public CglibProxy(Object bean) {
        this.bean = bean;
    }
    public Object getProxy(){
        //设置需要创建子类的类
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback(this);
        //通过字节码技术动态创建子类实例
        return enhancer.create();
    }
    //实现MethodInterceptor接口方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String methodName = method.getName();
        if (methodName.equals("wakeup")){
            System.out.println("早安~~~");
        }else if(methodName.equals("sleep")){
            System.out.println("晚安~~~");
        }
        //调用原bean的方法
        return method.invoke(bean,args);
    }
}

Execution code:

public static void main(String[] args) {
    CglibProxy proxy = new CglibProxy(new Student("张三"));
    Student student = (Student) proxy.getProxy();
    student.wakeup();
    student.sleep();
    proxy = new CglibProxy(new Doctor("王教授"));
    Doctor doctor = (Doctor) proxy.getProxy();
    doctor.wakeup();
    doctor.sleep();
    proxy = new CglibProxy(new Dog("旺旺"));
    Dog dog = (Dog) proxy.getProxy();
    dog.wakeup();
    dog.sleep();
    proxy = new CglibProxy(new Cat("咪咪"));
    Cat cat = (Cat) proxy.getProxy();
    cat.wakeup();
    cat.sleep();
}

explain:

Cglib is used as the proxy here, and its idea is similar to that of jdk dynamic proxy. The original bean construction also needs to be passed in. The reasons are mentioned above, so I won’t repeat them here.

The key code is here

//设置需要创建子类的类
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();

It can be seen that Cglib created a subclass of the original bean "out of thin air", and pointed the Callback to this, which is the current object, which is the proxy object. Thus interceptthe method will be called. In interceptthe method, the execution of additional functions is carried out, and finally the corresponding method of the original bean is called.

When debugging the generated proxy object, we can also see that Cglib has generated a subclass of the original bean out of thin air:

5.javassist dynamic proxy

Javassist is an open source class library for analyzing, editing, and creating Java bytecodes, which can directly edit and generate bytecodes generated by Java. Compared with bcel, asm and other tools, developers can dynamically change the class structure or dynamically generate classes without knowing the virtual machine instructions.

In daily use, javassit is usually used to dynamically modify bytecode. It can also be used to implement the function of dynamic proxy.

Not much to say, it's still the same example, I use javassist dynamic proxy to implement it again

Create JavassitProxy as a unified proxy:

public class JavassitProxy {
    private Object bean;
    public JavassitProxy(Object bean) {
        this.bean = bean;
    }
    public Object getProxy() throws IllegalAccessException, InstantiationException {
        ProxyFactory f = new ProxyFactory();
        f.setSuperclass(bean.getClass());
        f.setFilter(m -> ListUtil.toList("wakeup","sleep").contains(m.getName()));
        Class c = f.createClass();
        MethodHandler mi = (self, method, proceed, args) -> {
            String methodName = method.getName();
            if (methodName.equals("wakeup")){
                System.out.println("早安~~~");
            }else if(methodName.equals("sleep")){
                System.out.println("晚安~~~");
            }
            return method.invoke(bean, args);
        };
        Object proxy = c.newInstance();
        ((Proxy)proxy).setHandler(mi);
        return proxy;
    }
}

Execution code:

public static void main(String[] args) throws Exception{
    JavassitProxy proxy = new JavassitProxy(new Student("张三"));
    Student student = (Student) proxy.getProxy();
    student.wakeup();
    student.sleep();
    proxy = new JavassitProxy(new Doctor("王教授"));
    Doctor doctor = (Doctor) proxy.getProxy();
    doctor.wakeup();
    doctor.sleep();
    proxy = new JavassitProxy(new Dog("旺旺"));
    Dog dog = (Dog) proxy.getProxy();
    dog.wakeup();
    dog.sleep();
    proxy = new JavassitProxy(new Cat("咪咪"));
    Cat cat = (Cat) proxy.getProxy();
    cat.wakeup();
    cat.sleep();
}

explain:

Familiar formula, familiar taste, the general idea is similar. Also pass in the original bean construction. It can be seen that javassist is also solved by generating subclasses "out of thin air". At the end of the code, the target method of the original bean is called to complete the proxy.

The characteristic of javaassit is that you can use filter to set the method of the required proxy, and it can be constructed like a Criteria constructor. For other codes, if you carefully read the previous code demonstrations, you should be able to understand them easily.

6. ByteBuddy dynamic proxy

ByteBuddy, the bytecode buddy, is awesome when you hear it.

ByteBuddy is also a well-known open source library. Like Cglib, it is also based on ASM. There is also a more famous library called Mockito. I believe that many people have used it to write test cases. Its core is based on ByteBuddy. It can dynamically generate mock classes, which is very convenient. In addition, another big application of ByteBuddy is the java agent, whose main function is to intercept the class before it is loaded and insert its own code.

ByteBuddy is so powerful, it's an artifact. Can be applied in many scenarios. But here, we only introduce the use of ByteBuddy as a dynamic proxy. Regarding other usage methods, you may have to write a special article to describe it. Here, dig a hole for yourself first.

Come on, it is still a familiar example, a familiar formula. Let's implement the previous example again with ByteBuddy

Create ByteBuddyProxy as a unified proxy:

public class ByteBuddyProxy {
    private Object bean;
    public ByteBuddyProxy(Object bean) {
        this.bean = bean;
    }
    public Object getProxy() throws Exception{
        Object object = new ByteBuddy().subclass(bean.getClass())
                .method(ElementMatchers.namedOneOf("wakeup","sleep"))
                .intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean)))
                .make()
                .load(ByteBuddyProxy.class.getClassLoader())
                .getLoaded()
                .newInstance();
        return object;
    }
    public class AopInvocationHandler implements InvocationHandler {
        private Object bean;
        public AopInvocationHandler(Object bean) {
            this.bean = bean;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if (methodName.equals("wakeup")){
                System.out.println("早安~~~");
            }else if(methodName.equals("sleep")){
                System.out.println("晚安~~~");
            }
            return method.invoke(bean, args);
        }
    }
}

Execution code:

public static void main(String[] args) throws Exception{
    ByteBuddyProxy proxy = new ByteBuddyProxy(new Student("张三"));
    Student student = (Student) proxy.getProxy();
    student.wakeup();
    student.sleep();
    proxy = new ByteBuddyProxy(new Doctor("王教授"));
    Doctor doctor = (Doctor) proxy.getProxy();
    doctor.wakeup();
    doctor.sleep();
    proxy = new ByteBuddyProxy(new Dog("旺旺"));
    Dog dog = (Dog) proxy.getProxy();
    dog.wakeup();
    dog.sleep();
    proxy = new ByteBuddyProxy(new Cat("咪咪"));
    Cat cat = (Cat) proxy.getProxy();
    cat.wakeup();
    cat.sleep();
}

explain:

The idea is still the same as before. By carefully observing the code, ByteBuddy also uses the method of creating subclasses to realize dynamic proxy.

7. What is the performance of various dynamic agents

The implementation of the same example for the four dynamic agents was introduced earlier. There are two types of agents:

  • JDK dynamic proxy adopts the mode of interface proxy, the proxy object can only be assigned to the interface, allowing multiple interfaces
  • Cglib, Javassist, and ByteBuddy all adopt the subclass proxy mode. The proxy object can be assigned to the interface or copied to the concrete implementation class.

Spring5.X and Springboot2.X only use Cglib as the implementation of dynamic proxy. Does that mean that cglib has the best performance?

I did a simple and rough experiment here, directly performing the above 4 pieces of execution code on a single-threaded synchronous loop multiple times, and using the time-consuming to determine the performance of the 4 of them. Should be able to see some clues.

JDK PROXY循环10000遍所耗时:0.714970125秒
Cglib循环10000遍所耗时:0.434937833秒
Javassist循环10000遍所耗时:1.294194708秒
ByteBuddy循环10000遍所耗时:9.731999042秒

The result of the execution is as above

Judging from the execution results, cglib is indeed the best. As for why ByteBuddy executes so slowly, it may not necessarily be due to the poor performance of ByteBuddy. It may also be that there is a problem with my test code writing and I have not found the correct method. So this can only be used as a rough reference.

It seems that Spring's choice of Cglib still makes sense.

Dynamic proxy technology is an artifact for someone who often writes open source or middleware. This feature provides a new solution. This makes the code more elegant and simple. Dynamic proxy is also of great help to understand the core idea of ​​spring. I hope that students who are interested in dynamic proxy technology can try to run the code in the example to strengthen their understanding.

2. The core of dynamic programming: reflection

1. Overview of Java reflection mechanism

Reflection (reflection) is the key to Java being regarded as a dynamic language. The reflection mechanism allows the program to obtain the internal information of any class by means of the Reflection API during execution, and can directly manipulate the internal properties and methods of any object.

Class c = Class.forName("java.lang.String");                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   

After the class is loaded, a Class type object (a class has only one Class object) is generated in the method area of ​​the heap memory, and this object contains the class structure information of the complete class. We can see the class structure by looking at this object. This object is like a mirror through which we can see the structure of the class, so we call it vividly: reflection

  • Normal way: introduce the required "package class" name --> instantiate through new --> get instantiated object
  • Reflection method: instantiate object --> getClass() method --> get the complete "package class" name

2. Functions provided by the Java reflection mechanism

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

3. Java reflection advantages and disadvantages

Advantages: Dynamic object creation and compilation can be realized, showing great flexibility

Cons: Has an impact on performance. Using reflection is basically an interpreted operation, we can tell the JVM, what we want to do and what does it satisfy us, this type of operation is always slower than doing the same operation directly

4. Main APIs related to reflection

  • java.lang.Class represents a class
  • java.lang.reflect.Method represents the method of the class
  • java.lang.reflect.Field represents a member variable of a class
  • java.lang.reflect.Constructor represents the constructor of the class

5. Understand the Class class and get the Class instance

First define an entity class

image-20220708190051431

main program

public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类Class对象                                                                                                           
Class<?> cl = Class.forName("com.sww.Refelection.User");
System.out.println(cl);
}    
输出:class com.sww.Refelection.User

A class has only one Class object in memory. After a class is loaded, the entire structure of the class will be encapsulated in the Class object

The following method is defined in the Object class, which will be inherited by all subclasses

public final native 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 name of the class can be obtained through object reflection

For each class, the JRE reserves an invariant object of type Class. A Class object contains a specific structure (class / interface / enum / annotation / primitive type / 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
  • Each class instance will remember which Class instance it was generated from
  • All loaded structures in a class can be completely obtained through Class
  • The Class class is the root of Reflection. For any class you want to dynamically load and run, the only Class object that gets the response first

Common methods of the Class class

method name Function Description
static Class forName(String name) Returns the Class object of the specified class name name
Object newInstance() Call the default constructor, returning an instance of the Class object
getName() Returns the name of the entity (class, interface, array class, or void) represented by this Class object
Class getSuperClass() Returns the Class object of the parent class of the current Class object
Class[] getInterfaces() Get the interface of the current Class object
ClassLoader getClassLoader() Returns the class loader for this class
Constructor[] getConstructors() returns an array containing some Constructor objects
Method getMethod(String name, Class… T) Returns a Method object whose parameter type is paramType
Field[] getDeclaredFields() Returns an array of Field objects

6. Get an instance of the Class class

public static void main(String[] args) throws Exception {
// 方式一:forName 获得
Class aClass = Class.forName("com.java.demo.reflect.User");
System.out.println(aClass);
// 方式二:通过对象获得
Class aClass1 = new User().getClass();
System.out.println(aClass1);
// 方式三:通过类名.class 获得
Class<User> aClass2 = User.class;
System.out.println(aClass2);
}      

7. All types of Class objects

ublic static void main(String[] args) {
Class objectClass = Object.class;            // 类
Class comparableClass = Comparable.class;    // 接口
Class aClass = String[].class;               // 一维数组
Class aClass1 = int[][].class;               // 二维数组
Class overrideClass = Override.class;        // 注解
Class elementTypeClass = ElementType.class;  // 枚举
Class integerClass = Integer.class;          // 基本数据类型
Class voidClass = void.class;                // void
Class classClass = Class.class;              // Class
System.out.println(objectClass);
System.out.println(comparableClass);
System.out.println(aClass);
System.out.println(aClass1); 
System.out.println(overrideClass);
System.out.println(elementTypeClass);
System.out.println(integerClass); 
System.out.println(voidClass); 
System.out.println(classClass);                                                                                                     
}                                                                                                                               

8. Java memory analysis

9. Class loading and ClassLoader

  • 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
  • Linking: the process of merging the binary code of a Java class into the running state of the JVM
  • Verification: Ensure that the loaded class information conforms to the JVM specification and there are no security issues
  • Preparation: the stage of formally allocating memory for class variables (static) and setting the default initial value of class variables, all of which will be allocated in the method area
  • Resolution: The process of replacing symbolic references (constant names) in the virtual machine constant pool with direct references (addresses)
  • initialization:
  • The process of executing the class constructor <clint>() method. The class constructor <clint>() method is generated by combining the assignment actions of all class variables in the class automatically collected at compile time and the statements in the static code block (class constructor It is for constructing class information, not for constructing objects of this class)
  • When initializing a class, if you find that its parent class has not been initialized, you need to trigger the initialization of its parent class first
  • The virtual machine ensures that the <clint>() method of a class is properly locked and synchronized in a multi-threaded environment
public static void main(String[] args) {
/**
* 1、加载到内存,会产生一个类对应的 Class 对象
* 2、链接,链接结束后 m = 0
* 3、初始化         
<clint>() {   
System.out.println("A 类静态代码块初始化");
m = 300;     
m = 100;       
}        
m = 100;    
*/  
A a = new A();
System.out.println(A.m);
}         
}     
class A {     
static {
System.out.println("A 类静态代码块初始化"); 
m = 300;
}  
static int m = 100;  
public A() {
System.out.println("A 类的无参构造函数初始化");
}                                                                                           
------------------------------------------------------------------------                                                         
A 类静态代码块初始化                                                                                                                 
A 类的无参构造函数初始化
100

10. Obtain the complete structure of the runtime class & dynamically create the object execution method

Field、Method、Constructor、SuperClass、Interface、Annotation

  • All interfaces implemented
  • inherited parent class
  • all constructors
  • all methods
  • All Fields
  • annotation

11. Performance comparative analysis

setAccessible

  • Method and Field, Constructor objects have setAccessible() method
  • setAccessible is a switch to enable and disable access security checks
  • If the parameter value is true, it indicates that the reflected object should cancel the Java language access check when using it
  • Improve the efficiency of reflection. If reflection must be used in the code and the statement needs to be called frequently, please set it to true
  • Makes private members that are otherwise inaccessible accessible
  • A parameter value of false indicates that the reflected object should implement Java language access checks
// 普通方式调用
public static void test01() {
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行 1 亿次:" + (endTime - startTime) + "ms");
}  
// 反射方式调用    
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c = user.getClass();
Method getName = c.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行 1 亿次:" + (endTime - startTime) + "ms");
 }
// 反射方式调用,关闭检测 
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 
User user = new User();
Class c = user.getClass();
Method getName = c.getDeclaredMethod("getName", null);
getName.setAccessible(true); 
long startTime = System.currentTimeMillis(); 
for (int i = 0; i < 100000000; i++) { 
getName.invoke(user, null);
}       
long endTime = System.currentTimeMillis();
System.out.println("关闭检测方式执行 1 亿次:" + (endTime - startTime) + "ms");
}      
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();       
test02(); 
test03();        
}            
---------------------------------------------------------------------
普通方式执行 1 亿次:4ms 
反射方式执行 1 亿次:119ms 
关闭检测方式执行 1 亿次:87ms   
折叠 

12. Manipulate generics through reflection

  • Java uses the mechanism of generic erasure to introduce generics. The generics in Java are only used by the compiler javac to ensure data security and avoid the problem of forced type conversion. However, once the compilation is completed, all and generics All related types are erased
  • In order to manipulate these types through reflection, Java has added ParameterizedType, GenericArrayType, TypeVariable and WildcardType to represent types that cannot be attributed to the Class class but have the same name as the original type
  • ParameterizedType: Indicates a parameterized type, such as Collection<String>
  • GenericArrayType: Represents an array type of parameterized type or type variable when representing an element type
  • TypeVariable: is the common parent interface of various types of variables
  • WildcardType : represents a wildcard type expression
public void test01(Map<String, User> map, List<User> list) {
    System.out.println("test01");
}
public Map<String, User> test02() {
    System.out.println("test02");
    return null;
}
// 通过反射获取泛型
public static void main(String[] args) throws NoSuchMethodException {
    Method test01 = Test01.class.getMethod("test01", Map.class, List.class);
    // 获取通用参数类型
    Type[] genericParameterTypes = test01.getGenericParameterTypes();
    // 迭代遍历
    for (Type genericParameterType : genericParameterTypes) {
        System.out.println("# " + genericParameterType);
        if (genericParameterType instanceof ParameterizedType) {
            // 获取实际参数类型
            Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
    Method test02 = Test01.class.getMethod("test02", null);
    Type genericReturnType = test02.getGenericReturnType();
    if (genericReturnType instanceof ParameterizedType) {
        Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument);
        }
    }
}

Note: If the invoke method provides wrong parameters, an exception will be thrown, so an exception handler must be provided. It is recommended to use the invoke method only when necessary for the following reasons:

  • 1. The parameters and return value of the invoke method must be of Object type, which means that multiple type conversions must be performed
  • 2. Calling methods through reflection is significantly slower than calling methods directly

13. Reflection method

getConstructor(Class<?>… parameterTypes)

Get a specific public constructor for a class. The parameter is the object of the corresponding Class of the method parameter

public class ReflectDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Constructor<CaseMethod> constructor = CaseMethod.class.getConstructor(int.class, String.class);
        System.out.println(constructor);
    }
}
class CaseMethod{
    private int id;
    private String name;
    private int cap;
    public CaseMethod(int id,String name){
        this.id = id;
        this.name = name;
    }
    public CaseMethod(int cap){
    }
}

getConstructors()

Get all public constructors of the class

public static void main(String[] args) {
        Constructor<?>[] constructors = CaseMethod.class.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
    }
}
class CaseMethod{
    private int id;
    private String name;
    private int cap;
    public CaseMethod(int id,String name){
        this.id = id;
        this.name = name;
    }
    public CaseMethod(int cap){
    }

getDeclaredConstructor(Class<?>… parameterTypes)

    public static void main(String[] args) throws NoSuchMethodException {
        // 注意:由于private修饰的CaseMethod构造函数没有参数,所以getDeclaredConstructor()可以为空
        // 默认的getDeclaredConstructor(Class<?>... parameterTypes)方法是要传参数类型的
        Constructor<CaseMethod> constructor = CaseMethod.class.getDeclaredConstructor();
        Constructor<CaseMethod> declaredConstructor = CaseMethod.class.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor);
        System.out.println(declaredConstructor);
    }
}
class CaseMethod{
    private int id;
    private String name;
    private int cap;
    private CaseMethod(){
    }
    public CaseMethod(int id,String name){
        this.id = id;
        this.name = name;
    }

getDeclaredConstructors()

    public static void main(String[] args) throws NoSuchMethodException {
        Constructor<?>[] constructors = CaseMethod.class.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
    }
}
class CaseMethod{
    private int id;
    private String name;
    private int cap;
    private CaseMethod(){
    }
    public CaseMethod(int id,String name){
        this.id = id;
        this.name = name;
    }

14. Use newInstance to create an instance

public class ReflectDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<CaseMethod> constructor = CaseMethod.class.getConstructor(String.class);
        CaseMethod caseMethod = constructor.newInstance("Lisa");
        System.out.println(caseMethod);
    }
}
class CaseMethod{
    private String name;
    public CaseMethod(String name){
        this.name = name;
    }
    @Override
    public String toString() {
        return "CaseMethod{" +
                "name='" + name + ''' +
                '}';
    }
}

Summarize

In Java's method of obtaining a reference to a Class object, the Class.forName() method automatically initializes the Class object, but the .class method does not. The initialization of .class is delayed until the first reference of a static method or a non-constant static field.

Guess you like

Origin blog.csdn.net/m0_64420071/article/details/127615393