1. Reflection
Reflection is the key to the magic of JAVA.
Reflection mechanism allows the program 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 type Class is generated in the method area of heap memory. All classes we define are Class objects.
A Class has only one Class object. The objects of the Class we define are objects belonging to Class objects.
This Class object contains the complete result information of the Class. You can get the structure of the Class by getting this object. This object is like a mirror. Seeing the structure of the Class through this mirror is called reflection.
The process of obtaining the structure of a class through reflection:
Instantiate the object - > getClass method to obtain the bytecode file - > obtain the completed structure
Do you remember the java running process at the beginning?
Compile the. java file through javac.exe to get the. class file.
The getClass() method is to get the. Class file. What kind of file is this? See the following. The following is a commonly used class Animal.java at the beginning, and the Animal.class file generated through javac.exe.
You can see that this is a binary file. This is opened using the sublim editor. We can't understand these binaries at all. But it doesn't matter. The program can understand. This file contains all the information of the Animal class.
What can you do with reflection?
During operation, you can do the following things, including but not limited to:
-
Determine the class to which any object belongs
-
Construct an object of any class
-
Judge the member variables and methods of any class
-
Get generic information
-
Call the member variables and methods of any object
-
Processing annotations
-
Generate dynamic proxy
1.1 main API
java.lang.Class class
This is a class, which is capitalized, and the keyword is lowercase. java is strictly case sensitive.
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement
Class is used to describe a general class. Above is the class header. You can see that this class implements four interfaces.
java.lang.reflect.Method class: a class that describes the methods of a class
public final class Method extends Executable {}
The java.lang.reflect.Field class describes the class of class member variables
public final class Field extends AccessibleObject implements Member {}
The java.lang.reflect.Constructor class describes the class of the constructor of the class
public final class Constructor<T> extends Executable {}
wait.
Except that Class class is under lang package, all other classes are under reflect package, a sub package of lang package. These classes are basically defined as final. That is, it cannot be inherited.
public class Person { // Private property private String name; public int age; public void show(){ System.out.println("Public method: This is Person Class show Method printed"); } // Private method private String showNational(String str){ System.out.println("Private method: This is Person Class showNational Method printed:" + str); return str; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void setAge(int age) { this.age = age; } public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } // Private constructor private Person(String name) { this.name = name; } }
There is such a Person class as above. Let's try how to use reflection
// We can do these things without reflection // 1. Create an object of the Person class Person p1 = new Person("Tom", 12); // 2. Call public properties and methods through objects. p1.age = 10; System.out.println(p1); p1.show(); // Outside the Person class, private structures cannot be called through objects. Including private properties, methods, constructors.
Use reflection to do the same thing as above.
// Try using reflection // The Person class has an attribute class, which is an object of the class class. Class clazz = Person.class; // Get constructor Constructor cons = clazz.getConstructor(String.class, int.class); // create object Object tom = cons.newInstance("Tom", 12); // Strong rotation Person p = (Person) tom; System.out.println(tom.toString()); // Calling properties and methods through reflection // Call properties Field age = clazz.getDeclaredField("age"); age.set(p, 10); // Call the show method Method show = clazz.getDeclaredMethod("show"); show.invoke(p);
In addition to the above functions, reflection can also call private properties, methods and constructors that cannot be called in ordinary ways.
If reflection can only do things in the same way as ordinary ways, there is no need for reflection.
// The Person class has an attribute class, which is an object of the class class. Class clazz = Person.class; // Calling a private constructor using a clazz object Constructor cons1 = clazz.getDeclaredConstructor(String.class); // Set the private that can be accessed cons1.setAccessible(true); // create object Object obj = cons1.newInstance("Jerry"); // Strong turn back to Person object Person jerry = (Person) obj; // Call private property Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(jerry, "jerry_big"); System.out.println(jerry); // Call private method Method showNational = clazz.getDeclaredMethod("showNational", String.class); showNational.setAccessible(true); String str = (String) showNational.invoke(jerry, "Reflection call private hahaha"); System.out.println(str); // output // Person{name='jerry_big', age=0} // Private method: This is printed by the showNational method of the Person class: Reflection calls private hahaha // Reflection call private hahaha
As can be seen from the above code, it is mainly obtained through the class name point class. Whether private or shared, it can be obtained. However, if it is private, it is necessary to call setAccessible method to obtain permission. When a method with a return value is called, the return value of the invoke method is the return value of the original method.
1.2 thinking
1. Reflection can call private things. Then, in object-oriented, the encapsulation of classes, that is, permissions. Does it fail? Will this be contradictory?
There is no contradiction. These two requirements are needed in our actual development. Private is to tell you not to use it. Common is to tell you that it is provided for you. But it must be used, which is also possible.
2. Reflection can do the same things as ordinary calls. Is it unnecessary to use ordinary calls?
In terms of code quantity, there are obviously fewer ordinary calls and clearer logic. There is no trouble of coercion. Reflection is more about providing a dynamic.
When ordinary calls cannot meet the requirements, such as the runtime, it knows what to call. Then you need to use reflection.
1.3 Class
In the code of 1.1, reflection is basically operated through Class objects.
It is necessary to study this Class.
When a class is compiled into a. Class bytecode file. Use java.exe to interpret and run the bytecode file. It is equivalent to loading the bytecode file into memory. This process is class loading.
The classes loaded into memory are called runtime classes. This runtime Class is the object of Class.
Class clazz = Person.class;
We get the object of class class by class name. Class. In fact, the class name point class represents the class itself. But you can't write the class name directly, otherwise it's a type of code. In order to distinguish from types, add an attribute class to each class, and use the class name point class to represent the bytecode file. When loaded into memory, it becomes a class object.
There are more ways to get objects of Class than the above. And Class has generics
Class<Person> clazz = Person.class; // Method 2: call the getClass method through the object of the runtime class Person p1 = new Person(); Class aClass = p1.getClass();
Method 3: use the static method forName of Class. The full Class name needs to be passed in to prevent name conflicts
Class clazz = Class.forName("com.xgzit.JavaBaseStudy.reflectStudy.Person");
Method 4: use class loader
// First, get the object of ClassLoader class through the current class // Then use this object to load the Class to be obtained, and you will get the Class object. ClassLoader classLoader = TestMain.class.getClassLoader(); Class clazz = classLoader.loadClass("com.xgzit.JavaBaseStudy.reflectStudy.Person");
The runtime Class loaded into memory will be cached for a certain time. No matter how many methods are used, the objects of the same Class will be obtained.
Just like there is only one in Beijing, you can get to Beijing by different means of transportation
Of the above four methods, mode 3 is the most used. Because mode 1 is dead. If it is a class that does not exist at present, the compilation will report an error, which is not flexible enough.
There are objects in mode 2. Generally, there is no reflection. Method 3 is flexible. Even if the full class name does not exist at present, no error will be reported. Only when it does not exist at runtime will an error be reported.
java does not only have classes, but also interfaces, arrays, enumerations, annotations, basic data types, etc. are these loaded as objects of Class class?
In real time. void actually counts.
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;
The above nine examples are correct.
int[] a = new int[10]; int[] b = new int[100]; Class c1 = a.getClass(); Class c2 = b.getClass(); System.out.println(c1 == c2); // true
The above code shows that as long as the dimensions and types of arrays are consistent, such as 1-dimensional arrays of int type, their Class objects are the same.
A loading process of class:
When a program actively uses a class, if the class has not been loaded into memory, the system will initialize the class through the following three steps.
Class loading and ClassLoader
The function of class loading: load the contents of class bytecode file into memory, convert these data into the runtime data structure of the method area, and then generate a class object representing this class in the heap as the access entry of class data in the method area.
Class caching: the standard javaSe class loader can find classes as required, but once a class is loaded into the class loader, it will maintain loading (caching) for a period of time, but the JVM garbage collection mechanism can recycle these class objects.
There are four types of class loaders.
The boot class is responsible for the core library, the extension class is to load the imported jar package, and the system class is to load the classes written by ourselves. Customized ones are not introduced here.
// The class loader that gets the custom class is the system class loader ClassLoader classLoader = TestMain.class.getClassLoader(); System.out.println(classLoader); // Gets that the parent loader of the system class loader is an extension class loader ClassLoader parent = classLoader.getParent(); System.out.println(parent); // Get the parent loader of the extension class is the boot class loader, which cannot be obtained directly!!! ClassLoader parentParent = parent.getParent(); System.out.println(parentParent); // output // sun.misc.Launcher$AppClassLoader@18b4aac2 // sun.misc.Launcher$ExtClassLoader@4fca772d // null
The String class is the core library, which is loaded by the boot class loader. You can't get the class loader of String class directly.
1.4 loading configuration files
// The characteristic of properties is that the key value is String Properties pros = new Properties(); File file = new File("jdbc.properties"); // The files in idea are under the current Module by default FileInputStream fis = new FileInputStream(file); pros.load(fis); String name = pros.getProperty("name"); String password = pros.getProperty("password"); System.out.println(name);
The above code is the way to load the configuration file mentioned earlier.
Using ClassLoader can also be used to load configuration files
// The characteristic of properties is that the key value is String ClassLoader loader = TestMain.class.getClassLoader(); InputStream stream = loader.getResourceAsStream("jdbc1.properties"); Properties properties = new Properties(); properties.load(stream); String name = properties.getProperty("name"); System.out.println(name);
The classLoader object calls the getResourceAsStream method. The default file is in the src directory of the current module. But I failed the test. I don't know what the hell
1.5 creating objects of runtime classes
The class loaded into memory is the runtime class.
Class<Person> clazz = Person.class; // Create the object of the corresponding runtime class Person o = clazz.newInstance(); System.out.println(o); // Person{name='null', age=0}
First, you need to get the Class object of the corresponding Class. Call the newInstance method through the Class object. Create an object.
newInstance is actually an empty argument constructor for the call. Therefore, an empty parameter constructor must be provided. Another problem is permissions.
If the null parameter constructor permission is private, it cannot be called, and an illegal access exception will be reported. Usually, the permission is public
-
The object that creates the runtime class needs to provide an empty parameter constructor for this class
-
The permission of this null parameter constructor is generally public
In order to write general-purpose code, you need to provide an empty parameter constructor. In fact, you can get the parameterized constructor through getdeclaraedconstructor to create objects.
However, the parameter constructors of each class may have different parameters, so it is difficult to write general code, while the empty parameter constructors are the same without parameters. If there is an attribute, it will be added later in the form of attribute assignment.
public static void main(String[] args) throws Exception{ 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.xgzit.JavaBaseStudy.reflectStudy.Person"; break; default: break; } Object instance = getInstance(classPath); System.out.println(instance); } // Get object public static Object getInstance(String classPath) throws Exception{ Class<?> aClass = Class.forName(classPath); return aClass.newInstance(); }
The above code reflects the dynamic nature of reflection. Compile time does not know what object to. Only when running.
The getInstance method creates an object based on the passed in full class name. If the full class name passed in is different, the object created will be different.
The random number + switch structure is used to control the difference of classPath, so as to dynamically create different objects.
1.6 get the complete structure of the class
For clarity, some preparatory work is needed.
The first is annotation: previously, annotation was mainly used in reflection. Now let's use it. Here is a custom annotation
@Repeatable(value = MyAnnotations.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) public @interface MyAnnotation { // Defining an attribute is a bit like a method, but it is actually an attribute. The return value is an attribute type. If you need to receive multiple attribute values, you can define it as an array String[] value() default "Ha ha ha"; }
A custom interface:
public interface MyInterface { void info(); }
A parent class with generics
public class Creature<T> implements Serializable { private static final long serialVersionUID = 1L; private char gender; public double weight; private void breath() { System.out.println("Biological respiration"); } public void eat() { System.out.println("Creatures eat"); } }
A specific class:
@MyAnnotation(value = "hi") public class FullPerson extends Creature<String> implements Comparable<String>, MyInterface { private String name; int age; public int id; public FullPerson() {} private static int staticMethodShow(int num){ System.out.println("Private static method" + num); return num; } @MyAnnotation(value = "Private parametric structure") private FullPerson(String name){ this.name = name; } FullPerson(String name, int age){ this.name = name; this.age = age; } @MyAnnotation() private String show(String str) { System.out.println("My nationality is"+str); return str; } public String display(String interest) throws NullPointerException, ClassCastException{ System.out.println("My interest is"+interest); return interest; } @Override public int compareTo(String o) { return 0; } @Override public void info() { System.out.println("I am a person"); } @Override public String toString() { return "FullPerson{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }
The FullPerson class is annotated and has generics. It inherits the creation class and implements two interfaces. mub
The goal is to get the structure of the FullPerson class at runtime.
-
getFields method of Class
Class<FullPerson> clazz = FullPerson.class; // get attribute Field[] fields = clazz.getFields(); for (Field field : fields) { System.out.println(field); } // output // public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.id // public double com.xgzit.JavaBaseStudy.reflectStudy.Creature.weight
First, there must be a Class object with a runtime Class. All public properties can be obtained by using the getFields method. This involves the issue of permissions. You can't get properties without permission directly. All public attributes here include not only their own, but also the public attributes of all their parent classes.
1.6.1 properties
-
Getdeclaraedfields method of Class
Class<FullPerson> clazz = FullPerson.class; // get attribute Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } // output // private java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.name // int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.age // public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.id
The above code calls the getdeclaraedfields method. This method obtains all the attributes of the current class definition, excluding its parent class, and the obtained attributes ignore permissions.
-
getModifiers method gets the Modifier of the property, and the return value is of type int. That is, a number that represents a permission name. The corresponding relationship between this number and permission is defined in the Modifier class. The Modifier's static method toString can be used to translate the Modifier of permission back.
Class<FullPerson> clazz = FullPerson.class; // get attribute Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 1. Get permission modifier int modifier = field.getModifiers(); System.out.println(modifier); // Modifier's toString method can translate int numbers into strings System.out.println(Modifier.toString(modifier)); } // output 2 private 0 1 public
You can see that 2 is private; 0 is the default. The default is not to write. 1 is public.
-
Public class of Field class <? > The GetType () method gets the type of the property
Class<FullPerson> clazz = FullPerson.class; // get attribute Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // Get type Class<?> type = field.getType(); System.out.println(type); } // output class java.lang.String int int
You can see the type of property obtained.
-
String getName() method of Field class: get variable name
Class<FullPerson> clazz = FullPerson.class; // get attribute Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // Get variable name String name = field.getName(); System.out.println(name); } // output name age id
1.6.2 method
-
getMethods method of Class: get all public methods, including those of the parent Class
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); } // output public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.String) public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.Object) public void com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.info() public java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.display(java.lang.String) public void com.xgzit.JavaBaseStudy.reflectStudy.Creature.eat() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
-
Getdeclaraedmethods method of Class: all methods of the current Class, ignoring permissions, excluding those of the parent Class
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); } // output public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.String) public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.Object) public void com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.info() private java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.show(java.lang.String) public java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.display(java.lang.String)
-
The Annotation[] getAnnotations() Method of the Method class obtains all annotations of the Method
Note that this can only be used to obtain the annotation whose lifecycle @ Retention is RetentionPolicy.RUNTIME. Moreover, the annotation should be able to act on the method.
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Annotation[] annos = method.getAnnotations(); for (Annotation anno : annos) { System.out.println(anno); } } // output @com.xgzit.JavaBaseStudy.AnnotationStudy.MyAnnotation(value=[Ha ha ha])
-
int getModifiers() of the Method class gets the modifier of the Method. It is almost consistent with the modifier acquisition of attributes
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { int mod = method.getModifiers(); System.out.println(mod); System.out.println(Modifier.toString(mod)); } // output 1 public 4161 public volatile 1 public 1 public 2 private
1 stands for public, 4161 for public volatile and 2 for private
-
Method class <? > Getreturntype() gets the return value type of the method
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Class<?> returnType = method.getReturnType(); System.out.println(returnType); } // output int int void class java.lang.String class java.lang.String
-
Get the Method name by String getName() of the Method class
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { String name = method.getName(); System.out.println(name); } // output compareTo compareTo info show display
-
Method class <? > [] getparametertypes get parameter types
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { String name = method.getName(); System.out.print(name + "("); Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length != 0){ for (Class<?> parameterType : parameterTypes) { System.out.print(parameterType); } } System.out.println(")"); } // compareTo(class java.lang.String) compareTo(class java.lang.Object) info() display(class java.lang.String) show(class java.lang.String)
-
Method class <? > [] getexceptiontypes() method gets the exception thrown by the method
Class<FullPerson> clazz = FullPerson.class; // Acquisition method Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { String name = method.getName(); System.out.print(name +"\t"); Class<?>[] exceptionTypes = method.getExceptionTypes(); if (exceptionTypes.length != 0){ System.out.print("throws"); for (Class<?> exceptionType : exceptionTypes) { System.out.print(exceptionType); } } System.out.println(); } //output compareTo compareTo info display throwsclass java.lang.NullPointerExceptionclass java.lang.ClassCastException show
1.6.3 constructor
-
Constructor of Class <? > [] getconstructors () method to obtain the constructor of the runtime Class.
Note: the constructor here cannot obtain the parent class, but can only obtain its own public permission constructor
Class<FullPerson> clazz = FullPerson.class; // Get constructor Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } // output public com.xgzit.JavaBaseStudy.reflectStudy.FullPerson()
-
Constructor of Class <? > [] getdeclaraedconstructors() method obtains all constructors of the runtime Class itself, ignoring permissions
Class<FullPerson> clazz = FullPerson.class; // Get constructor Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } // output com.xgzit.JavaBaseStudy.reflectStudy.FullPerson(java.lang.String,int) private com.xgzit.JavaBaseStudy.reflectStudy.FullPerson(java.lang.String) public com.xgzit.JavaBaseStudy.reflectStudy.FullPerson()
You can also get the constructor's parameter list, modifiers and annotations. The method is similar to the method.
1.6.4 parent class
-
Class <? Super T > getsuperclass() method to get the parent class
Class<FullPerson> clazz = FullPerson.class; // Get parent class Class<? super FullPerson> superclass = clazz.getSuperclass(); System.out.println(superclass); // class com.xgzit.JavaBaseStudy.reflectStudy.Creature
The parent Class obtained is also an object of Class.
-
The Type getGenericSuperclass() method of Class gets the parent Class with generics. The return value is of type type. Type is an interface
Class<FullPerson> clazz = FullPerson.class; // Get parent class with generics Type superclass = clazz.getGenericSuperclass(); System.out.println(superclass); // output com.xgzit.JavaBaseStudy.reflectStudy.Creature<java.lang.String>
-
Gets the generic type of the parent class
Class<FullPerson> clazz = FullPerson.class; // Get parent class with generics Type superclass = clazz.getGenericSuperclass(); System.out.println(superclass.getClass()); // class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl // Strong rotation ParameterizedType para = (ParameterizedType) superclass; Type[] arguments = para.getActualTypeArguments(); for (Type argument : arguments) { System.out.println(argument.getTypeName()); // java.lang.String }
Why can I turn strongly? Because superclass is of this type. You can use superclass.getClass() to get the type of the runtime class. You know why you can turn strongly.
1.6.5 interface, package and annotation
-
Class<?> [] getinterfaces() gets the interface implemented by the runtime class. This does not get the interface implemented in the parent class.
Class<FullPerson> clazz = FullPerson.class; // Get the implemented interface Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface); } // output interface java.lang.Comparable interface com.xgzit.JavaBaseStudy.interfacepack.MyInterface
-
Package getPackage() gets the package where the runtime class is located
Class<FullPerson> clazz = FullPerson.class; // Get the package Package aPackage = clazz.getPackage(); System.out.println(aPackage); // output package com.xgzit.JavaBaseStudy.reflectStudy
-
Annotation[] getAnnotations() gets the annotation above the runtime class
Class<FullPerson> clazz = FullPerson.class; // Get the annotation above the class Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } // output @com.xgzit.JavaBaseStudy.AnnotationStudy.MyAnnotation(value=[hi])
This section obtains the complete structure of the Class, which is actually the use of the methods in the Class. It also involves Field Class, Method Class, Constructor Class, Package Class and Annotation Class. That is, each structure in a Class has a Class corresponding to it. Calling the methods of these classes can do the corresponding work. In essence, it is the knowledge of classes and objects.
1.7 call the specified properties and methods of the runtime class
1.7.1 specifying attributes
-
Field getField(String name) method of Class: get the specified attribute
This method can only get the properties of public permissions
Class<FullPerson> clazz = FullPerson.class; // Create an object for the runtime class FullPerson person = clazz.newInstance(); // Gets the specified property Field id = clazz.getField("id"); // Set the value of the current property. Parameter 1 is the object and parameter 2 is the value id.set(person, 1001); // Gets the property value of the object int o = (int) id.get(person); System.out.println(o); // 1001
-
Field getdeclaraedfield (string name) method of Class: get the specified attribute
This method can get all the properties of the runtime class and ignore the permissions
Class<FullPerson> clazz = FullPerson.class; // Create an object for the runtime class FullPerson person = clazz.newInstance(); // Gets the specified property Field name = clazz.getDeclaredField("name"); // Set permissions to get private name.setAccessible(true); // Set the value of the current property. Parameter 1 is the object and parameter 2 is the value name.set(person, "Tom"); // Gets the property value of the object String o = (String) name.get(person); System.out.println(o);
Note that setAccessible(true) must be set when accessing private properties. Otherwise, an error will be reported.
1.7.2 designation method
-
Method getmethod (string name, Class <? >... Parametertypes) method of Class: get the specified method
getMethod can only obtain methods with public permissions
Class<FullPerson> clazz = FullPerson.class; // Create an object for the runtime class FullPerson person = clazz.newInstance(); // Gets the specified method. The first parameter is the method name, and the following parameter is the formal parameter type of the method. Method display = clazz.getMethod("display", String.class); System.out.println(display); // output public java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.display(java.lang.String) throws java.lang.NullPointerException,java.lang.ClassCastException
-
Method getdeclaraedmethod (string name, class <? >... Parametertypes) gets the specified method
This method can obtain all permissions.
Class<FullPerson> clazz = FullPerson.class; // Create an object for the runtime class FullPerson person = clazz.newInstance(); // Gets the specified method Method show = clazz.getDeclaredMethod("show", String.class); System.out.println(show); // Set permissions to get private show.setAccessible(true); show.invoke(person, "haha"); // output private java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.show(java.lang.String) My nationality is haha
Similarly, the setAccessible(true) method must be called to use private permission methods.
The calling method is the method object show calling the invoke method.
Object invoke(Object obj, Object... args)
The first parameter of the invoke method is the object that invokes the method, followed by the parameters of the original method. The return value is the return value of the original method.
The following is an example of a static method:
Class<FullPerson> clazz = FullPerson.class; // Create an object for the runtime class FullPerson person = clazz.newInstance(); // Gets the specified method Method show = clazz.getDeclaredMethod("staticMethodShow", int.class); System.out.println(show); // Set permissions to get private show.setAccessible(true); show.invoke(person, 5); // Private static method 5 int invoke = (int) show.invoke(clazz, 5);//Private static method 5 System.out.println(invoke); // 5 // output private static int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.staticMethodShow(int) Private static method 5 Private static method 5 5
show.invoke(person, 5); Is to call static methods through objects. show.invoke(clazz, 5) is called through a class.
The second method is recommended.
In fact, this call is OK. Write null directly. Because this is a static method, you don't need an object to call it. show is obtained through clazz, and it is known that it is called through class objects.
show.invoke(null, 5); //Private static method 5
1.7.3 specifying constructors
-
getConstructor method gets the specified common permission constructor.
Class<FullPerson> clazz = FullPerson.class; Constructor<FullPerson> constructor = clazz.getConstructor(); System.out.println(constructor); // public com.xgzit.JavaBaseStudy.reflectStudy.FullPerson()
-
Getdeclaraedconstructor gets the specified constructor, ignoring permissions.
Class<FullPerson> clazz = FullPerson.class; Constructor<FullPerson> constructor = clazz.getDeclaredConstructor(String.class); System.out.println(constructor); // private com.xgzit.JavaBaseStudy.reflectStudy.FullPerson(java.lang.String)
If you want to use a private constructor, you also need to call the setAccessible(true) method
Class<FullPerson> clazz = FullPerson.class; Constructor<FullPerson> constructor = clazz.getDeclaredConstructor(String.class); constructor.setAccessible(true); FullPerson xxxx = constructor.newInstance("xxxx"); System.out.println(xxxx); // output FullPerson{name='xxxx', age=0, id=0}
We often use the Class object to directly call newInstance and call the parameterless constructor. This is more general.
2. Dynamic agent
The proxy model has been described earlier.
It is mainly static proxy. The characteristic is that the proxy class and the class of the target object are determined during compilation, which is not conducive to the expansion of the program. At the same time, each agent class can only serve one interface, so there must be too many agents in program development.
We prefer to use a proxy class to complete all the proxy functions.
That's dynamic proxy. When the program is running, dynamically create the proxy object of the target class as needed.
2.1 static agent
Review static agents.
An interface is required.
public interface ClothFactory { void produceCloth(); }
A static proxy class.
public class ProxyClothFactory implements ClothFactory{ private ClothFactory factory; @Override public void produceCloth() { System.out.println("Acting factory to do front work"); // The agent does its own core work factory.produceCloth(); System.out.println("Act as an agent for the factory to finish the work"); } public ProxyClothFactory(ClothFactory clothFactory){ this.factory = clothFactory; } }
A proxy class
public class QdClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Jordan factory produces a batch of sports shoes"); } }
use:
@Test public void test1() { QdClothFactory qd = new QdClothFactory(); ProxyClothFactory proxyClothFactory = new ProxyClothFactory(qd); proxyClothFactory.produceCloth(); }
Both the proxy class and the proxy class must implement the same interface.
2.2 dynamic agent
One interface
public interface Human { /** Gain faith**/ String getBelief(); /** Eat**/ void eat(String food); }
A proxy class
public class SuperMan implements Human{ @Override public String getBelief() { return "Superman saves the earth"; } @Override public void eat(String food) { System.out.println("Superman likes to eat"+food); } }
A proxy class:
Think about how to dynamically create a proxy class according to the proxy class loaded into memory?
When a method is called through the object of the proxy class, how to dynamically call the method with the same name in the proxy class?
public class ProxyFactory { /** Call this method to return a proxy class object**/ public static Object getProxyInstance(Object obj) { MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); // Use the static method newProxyInstance of Proxy to get the Proxy class object return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } }
The above proxy is a general method to obtain proxy class objects. You need to receive an object of a proxied class. Then we call the static method of the Proxy class newProxyInstance to get the proxy object. This newproxyinstance is an API provided by JDK. Take a look.
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //Check that the InvocationHandler implementation class object cannot be null, otherwise an exception will be thrown Objects.requireNonNull(h); // Get all the interfaces final Class<?>[] intfs = interfaces.clone(); // security check final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } // Use the provided class loader and interface to get the same class object Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // Get constructor final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } } private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { // The maximum number of interfaces cannot exceed 65535 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //If the proxy class defined by the given loader //If the given interface exists, this will only return a cached copy; //Otherwise, it creates a proxy class through ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
This method newProxyInstance finally creates a proxy class object for us dynamically according to the class loader of the proxy class, the interface implemented by the proxy class, and an InvocationHandler implementation class object provided by us.
Take a look at the InvocationHandler interface
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
This interface is relatively simple, with only one invoke Method. Receive 3 parameters. One is the Object, the second is the Method class Object, and the third is the parameter array of Object array type.
When we call a method using the obtained proxy class object, we will call the invoke method of the interface implementation class.
We need to provide such an implementation class object.
public class MyInvocationHandler implements InvocationHandler { /** Save proxied class object**/ private Object obj; /** Constructor to receive the proxied object**/ public MyInvocationHandler(Object obj){ this.obj = obj; } // When we call method a through the object of the proxy class, we will automatically call the invoke method here @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("The agent class does the front work"); // method.invoke is the core work done by the proxy class itself Object returnVal = method.invoke(obj, args); System.out.println("The agent class does the closing work"); return returnVal; } }
This implementation class needs to receive the object of the proxy class. Because this object needs to call its own method.
In the invoke method, like the static proxy, you can do some pre and post work. But in the middle, the proxy object must work by itself.
Object returnVal = method.invoke(obj, args);
Test:
// Agent superMan SuperMan superMan = new SuperMan(); Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); proxyInstance.eat("beauty"); // Proxy qdClothFactory QdClothFactory qdClothFactory = new QdClothFactory(); ClothFactory instance = (ClothFactory) ProxyFactory.getProxyInstance(qdClothFactory); instance.produceCloth();
It can be seen that the general method getProxyInstance for obtaining proxy objects can proxy not only superMan but also qdcloshfactory
Using dynamic proxies eliminates the need to create multiple proxy classes.
In real time, we found that in the ProxyFactory class, the InvocationHandler implementation class object we created only once. Can I use the anonymous implementation class of the interface? Yes. Code transformation
public class ProxyFactory { /** Call this method to return a proxy class object**/ public static Object getProxyInstance(Object obj) { // Use the static method newProxyInstance of Proxy to get the Proxy class object return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(obj, args); } }); } }
Is that it? Next, we will learn about the new features of JDK8. Use lambda expressions. It can be modified again.
public class ProxyFactory { /** Call this method to return a proxy class object**/ public static Object getProxyInstance(Object obj) { // Use the static method newProxyInstance of Proxy to get the Proxy class object return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), (proxy, method, args) -> method.invoke(obj, args)); } }
See the next section for details of lambda expressions.
2.3 brief introduction of AOP and dynamic agent
It is difficult to see the advantages of this dynamic Proxy in the Proxy and InvocationHandler described earlier. Let's take a look at the dynamic Proxy in Spring.
situation:
Assuming that the same code segment appears in three places, we generally think of extracting the same code into A method A, and then calling this method in three places.
This is the most common way we deal with the same code. However, these three code segments are coupled with the extracted method A.
The most ideal effect is that method A can be executed in these three code segments, and there is no need to call it directly by hard coding in the program.
example:
Write a general method class
public class GeneralMethod { public void method1(){ System.out.println("General method 1"); } public void method2(){ System.out.println("General method 2"); } }
Transform the ProxyFactory class.
public class ProxyFactory { /** Call this method to return a proxy class object**/ public static Object getProxyInstance(Object obj) { // Use the static method newProxyInstance of Proxy to get the Proxy class object return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), (proxy, method, args) -> { GeneralMethod humanUtil = new GeneralMethod(); humanUtil.method1(); Object invoke = method.invoke(obj, args); humanUtil.method2(); return invoke; }); } }
In fact, there is no difference, that is, some general things are done in front of and behind the proxy class.