1. Overview 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, a Class object is generated in the method area of heap memory (a Class has only one Class object), which contains the complete Class structure information. We can see the structure of the Class through this object. This object is like a mirror, through which we can see the structure of the Class. Therefore, we vividly call it reflection.
Examples of reflection
- Create a Person class
public class Person { private String name; public int age; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + 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() { } public Person(String name, int age) { this.name = name; this.age = age; } private Person(String name) { this.name=name; } public void show() { System.out.println("implement show method"); } private void say() { System.out.println("hollow word"); } }
- Use reflection
//Operation corresponding to person before reflection @Test public void demo1() { Person person = new Person("Zhang San", 19); person.age=20; System.out.println(person.getAge()); person.show(); } @Test public void demo2() throws Exception { Class personClass = Person.class; //Create an object of the person class through reflection Constructor constructor = personClass.getConstructor(String.class, int.class); Object tom = constructor.newInstance("tom", 10); Person tom1 = (Person) tom; System.out.println(tom1.toString()); //Invoke the specified methods and properties through reflection //Call properties Field age = personClass.getDeclaredField("age"); age.set(tom1,20); System.out.println(tom1.toString()); //Call method Method show = personClass.getDeclaredMethod("show"); show.invoke(tom1); System.out.println("=============================="); //Calling private structures through reflection Constructor constructor1 = personClass.getDeclaredConstructor(String.class); constructor1.setAccessible(true); Object jeck = constructor1.newInstance("jeck"); Person jeck1 = (Person) jeck; System.out.println(jeck1); //Calling private properties through reflection Field name = personClass.getDeclaredField("name"); name.setAccessible(true); name.set(jeck1,"jack123"); System.out.println(jeck1); //Calling private methods through reflection Method say = personClass.getDeclaredMethod("say"); say.setAccessible(true); Object invoke = say.invoke(jeck1); }
2. Understanding of java.lang.Class
- Class loading process
After the javac.exe command is used to query, one or more bytecode files (. End of class) will be generated
Then we use the java.exe command to parse and run a bytecode file, which is equivalent to loading a bytecode file into memory. This process is used as class loading. The class loaded into memory is called the runtime class, and this runtime class is used as an instance of class - In other words, an instance of Class corresponds to a runtime Class
- When the runtime class is loaded into memory, it will be cached for a certain time. Within this time, we can obtain this runtime class in different ways
2.1 four ways to obtain class instances (the first three need to be mastered)
@Test public void text3() throws ClassNotFoundException { //1. If a class is known, it can be obtained through the class attribute of the class Class<Person> clazz1 = Person.class; System.out.println(clazz1); //2. Always the object of a class, and get it through the getClass of the object Person person = new Person(); Class clazz2 = person.getClass(); System.out.println(clazz2); //3. The full Class name of a Class is known, and the Class can be obtained through the static method forName() of Class under the Class path Class clazz3 = Class.forName("com.blb.com.Person"); System.out.println(clazz3); ClassLoader classLoader = Person.class.getClassLoader(); Class<?> clazz4 = classLoader.loadClass("com.blb.com.Person"); System.out.println(clazz4); System.out.println(clazz1==clazz2); System.out.println(clazz1==clazz3); System.out.println(clazz1==clazz4); }
2.2 what types can have class objects
- Class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
- interface: interface
- []: array
- enum: Enumeration
- Annotation: annotation @ interface
- primitive type: basic data type
- void
3. Class loading process
- Load: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a java.lang.Class object representing this class as the access entry (i.e. reference address) of the class data in the method area. All class data that needs to be accessed and used can only be accessed through this class object. This loading process requires the participation of the class loader.
- Link: the process of merging the binary code of Java classes into the running state of the JVM.
- Verification: ensure that the loaded class information conforms to the JVM specification. For example, starting with cafe, there is no security problem
- Preparation: the stage of formally allocating memory for class variables (static) and setting the default initial value of class variables. These memory will be allocated in the method area.
- Resolution: the process of replacing the symbolic reference (constant name) in the virtual machine constant pool with a direct reference (address).
- initialization:
- The process of executing a class constructor () method. The class constructor () method is generated by automatically collecting the assignment actions of all class variables in the class at compile time and combining the statements in the static code block. (the class constructor is used to construct class information, not the constructor to construct the class object.).
- When initializing a class, if it is found that its parent class has not been initialized, the initialization of its parent class needs to be triggered first.
- Virtual chance ensures that the () methods of a class are locked and synchronized correctly in a multithreaded environment.
3.1 loader
4. Create a runtime object
4.1 newInstance
This method is called to create the object of the runtime class, and the empty parameter constructor of the runtime class is called internally
Object requirements for creating a runtime class with this method:
- The runtime must provide an empty argument constructor
- The access permission of the null parameter constructor should be sufficient. It is usually set to public
The requirement to provide a public null parameter constructor in a java bean
3. It is convenient to create objects of runtime classes through reflection
4. When subclasses inherit this running class u, when super() is called by default, ensure that the parent class has this constructor
@Test public void test4() throws InstantiationException, IllegalAccessException { Class<Person> personClass = Person.class; Person person = personClass.newInstance(); System.out.println(person); //Call a non empty parameter constructor //Create an object of the person class through reflection Constructor constructor = personClass.getConstructor(String.class, int.class); Object tom = constructor.newInstance("tom", 10); Person tom1 = (Person) tom; System.out.println(tom1.toString()); }
5. Get the complete structure of the runtime
5.1 the complete structure of the runtime can be obtained
- All interfaces implemented
- Inherited parent class
- All constructors
- All methods
- All fields
5.2 get attribute structure
- public Field[] getFields(): returns the public Field of the Class or interface represented by this Class object.
- Public Field [] getdeclaraedfields(): returns all fields of the Class or interface represented by this Class object.
In the Field method:
- public int getModifiers() returns the modifier of this Field as an integer
- public Class<?> Gettype() gets the property type of Field
- public String getName() returns the name of the Field.
5.3 get the method structure of runtime
- Public method [] getdeclaraedmethods(): get the methods declared in the current runtime class (excluding the methods declared in the parent class)
- public Method[] getMethods(): get the methods declared as public permissions in the current runtime class and all its parent classes
Method class:
- public Class<?> Getreturntype() gets all the return values
- public Class<?> [] getparametertypes() get all parameters
- public int getModifiers() get modifiers
- public Class<?> [] getexceptiontypes() get exception information
5.4 all constructors
- public Constructor[] getConstructors(): returns all public constructor methods of the Class represented by this Class object.
- Public constructor [] getdeclaraedconstructors(): returns all construction methods of the Class declaration represented by this Class object.
In the Constructor class:
- Get modifiers: public int getModifiers();
- Get method name: public String getName();
- Get the type of parameter: public class <? > [] getParameterTypes();
5.5 inherited parent class
public Class<? Super T > getsuperclass(): returns the class representing the parent class of the entity (class, interface, basic type) represented by this class.
5.6 all interfaces realized
public Class<?>[] getInterfaces()
Determines the interface implemented by the class or interface represented by this object.
5.7. Generic correlation
Get generic type of parent class: Type getGenericSuperclass()
Generic type: parametrizedtype
Get the actual generic type parameter array: getActualTypeArguments()
6. Call the specified structure of the runtime class
6.1. Call the specified method
Through reflection, the Method in the class is called and completed through the Method class. Steps:
- Get a Method object through the getMethod(String name,Class... parameterTypes) Method of Class class, and set the parameter type required for this Method operation.
- Then use Object invoke(Object obj, Object[] args) to call and pass the parameter information of the obj object to be set to the method
Object invoke(Object obj, Object ... args)
explain:
- Object corresponds to the return value of the original method. If the original method has no return value, null is returned
- If the original method is a static method, the formal parameter Object obj can be null
- If the original method parameter list is empty, Object[] args is null
- If the original method is declared as private, you need to explicitly call the setAccessible(true) method of the method object before calling the invoke() method to access the private method.
6.2 calling the specified attribute
In the reflection mechanism, you can directly operate the properties in the class through the Field class. You can set and obtain the property content through the set() and get() methods provided by the Field class.
- public Field getField(String name) returns the specified public Field of the Class or interface represented by this Class object.
- Public Field getdeclaraedfield (string name) returns the specified Field of the Class or interface represented by this Class object.
- In Field:
- public Object get(Object obj) gets the attribute content of this Field on the specified object obj
- public void set(Object obj,Object value) sets the attribute content of this Field on the specified object obj
On the use of setAccessible method
- Method, Field and Constructor objects all have setAccessible() methods.
- setAccessible switches that enable and disable access security checks.
- If the parameter value is true, it indicates that the Java language access check should be cancelled when the reflected object is used.
- Improve the efficiency of reflection. If reflection must be used in the code, and the code of this sentence needs to be called frequently, please set it to true.
- So that private members that cannot be accessed can also be accessed
- If the parameter value is false, it indicates that the reflected object should implement Java language access check.
7. Dynamic agent
7.1 principle of agent design pattern
-
Wrap the object with a proxy object and replace the original object with the proxy object. Any call to the original object is made through a proxy. The proxy object determines whether and when method calls are transferred to the original object.
-
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.
-
Usage of dynamic agent: debugging and remote method call
-
Advantages of dynamic agents over static agents:
All methods declared in the abstract role (Interface) are transferred to a centralized method of the calling processor. In this way, we can deal with many methods more flexibly and uniformly.
7.2 examples
7.2.1 static proxy
The proxy class and the proxied class are determined at run time
public interface ClothFactory { public void productShoe(); } //Proxy class class ClothFactoryA implements ClothFactory{ @Override public void productShoe() { System.out.println("A batch of clothes are being produced"); } } //proxy class class ProxyClothFactory implements ClothFactory{ private ClothFactory clothFactory; ProxyClothFactory(ClothFactory clothFactory) { this.clothFactory=clothFactory; } @Override public void productShoe() { System.out.println("start"); clothFactory.productShoe(); System.out.println("end"); } } class proxyStatic{ public static void main(String[] args) { ClothFactoryA clothFactoryA = new ClothFactoryA(); ProxyClothFactory proxyClothFactory = new ProxyClothFactory(clothFactoryA); proxyClothFactory.productShoe(); } }
7.2.2 dynamic agent
package com.blb.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Human { String belief(); void eat(String food); } //Proxy class class SupperHuman implements Human{ @Override public String belief() { return "I have a dream"; } @Override public void eat(String food) { System.out.println("I love eating"+food); } } /** * * 1.How to dynamically create a proxy class machine object according to the proxy class loaded into memory * 2.How to dynamically call the method with the same name in the proxy class when calling the object through the object of the proxy class */ class ProxyFactrory{ //According to this method, a proxy object is returned to solve problem 1 public static Object getProxyInstance(Object obj) { MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler implements InvocationHandler{ private Object object;//The proxy object needs to be assigned public void bind(Object o) { this.object=o; } //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 { //Method refers to the method called by the proxy class object, which is also the method to be called by the proxy class object //obj object of the proxied class Object invoke = method.invoke(object, args); //The return value of the above method is used as the return value of invoke() in the current class return invoke; } } public class ProxyTest { public static void main(String[] args) { SupperHuman supperHuman = new SupperHuman(); Human proxyInstance = (Human) ProxyFactrory.getProxyInstance(supperHuman); String belief = proxyInstance.belief(); System.out.println(belief); proxyInstance.eat("chicken"); } }