Java reflection mechanism
What is a class loader
When a Java program wants to use a class that has not been loaded into memory, the system initializes the class by loading, linking, and initializing it. Loading means reading a class file into memory and creating a class object for it. The system creates a Class object whenever any Class is used. Links verify that they have the correct internal structure and are compatible with other classes. Preparation is responsible for allocating memory for static members of the class and setting default initialization values. Parsing replaces symbolic references in tired binary data with direct references. Initializer Initialization of a class
Class initialization timing (when it is loaded into memory)
Create an instance of a Class access a static variable of a Class get a value assign a static variable use a static method of a Class use reflection to force the creation of a Class or interface initialize a subclass of a Class (the parent Class is loaded into memory first) use the java.exe command to run a main Class When any of the above conditions are met, The class will be initialized
The responsibilities of the class loader
Responsible for loading the. Class file into the memory and generating the class object. The root ClassLoader is responsible for loading the Java core classes. The root ClassLoader is responsible for loading the Java core classes. The root ClassLoader is responsible for loading the Java core classes. The Bootstrap is responsible for loading rt.jar (C code completion) Extension ClassLoader (C code completion). The Extension application class is in the jre/lib/ext directory (C code completion) The System ClassLoader (Java code completion) is responsible for loading the classes we wrote
What is reflection
Java reflection allows you to know all the properties and methods of any class in runtime; For any object, you can call any of its methods and properties; This ability to dynamically retrieve information and invoke methods on objects is called Java’s reflection mechanism. Reflection simply means dissecting a Class and retrieving its properties and methods, provided that the Class object is retrieved. An important part of the reflection mechanism is the “runtime”, which allows us to load, explore, and use.class files that are completely unknown at compile time while the program is running. In other words, a Java program can load a.class file whose name is not known until runtime, then learn of its complete construction and generate its object entities, or set values for its fields, or call its methods.
Several classes are provided in Java to describe various compiled objects
Java. Lang. A Class describes the compiled Java Class file object. Lang. Reflect. The Constructor is used to describe a Constructor in Java. Lang. Reflect. Field description Field (member variables) Java.lang.reflect. Method describes member methods
How do I get a class file object
Each class uses Object as its parent. The Object class method getClass() returns the class file Object of this class. The method returns a value of class type Object
Class returns the class file object for this class. Property runs as an object of type Class
Static method forName(String Class name) pass the String Class name to get the Class file object of this Class. The method return value is also an object of Class type
Class objects are equal no matter which way they are obtained.
Person p1 = new Person();
Class cc = p1.getClass();
System.out.println(cc);
Class cc2 = Person.class;
System.out.println(cc2);
Class cc3 = Class.forName("com.yxm.demo.demo0.Person");
System.out.println(cc3);
System.out.println(cc == cc2);
System.out.println(cc2 == cc3);
Copy the code
The results of
class com.yxm.demo.demo0.Person
class com.yxm.demo.demo0.Person
class com.yxm.demo.demo0.Person
true
true
Copy the code
The Person class has three constructors:
Public Person(){system.out.println ("Person class with no arguments "); } public Person(int a,int b,String s){system.out.println (" a:"+a+" b:"+b+" s:"+s); } private Person(int a){system.out.println ("Person class has a private construct: a:"+a); }Copy the code
Use reflection to get the constructor
public static void main(String[] args) throws Exception {
Class clazz = Person.class;
Constructor[] conarr = clazz.getDeclaredConstructors();
for (Constructor con : conarr) {
System.out.println(con);
}
Constructor cc = clazz.getConstructor();
Object oo = cc.newInstance();
Constructor cc2 = clazz.getConstructor(int.class, int.class, String.class);
Object oo2 = cc2.newInstance(1, 2, "haha");
Constructor cc3 = clazz.getDeclaredConstructor(int.class);
cc3.setAccessible(true);
Object oo3 = cc3.newInstance(1);
clazz.newInstance();
}
Copy the code
The output
public com.yxm.demo.demo0.Person() public com.yxm.demo.demo0.Person(int,int,java.lang.String) private Com. Yxm. Demo. Demo0. Person (int) Person class no parameters structure Person class has a structure: a: b: 1 2 s: haha private Person class have parameter structure: a: 1 Person class without structure parametersCopy the code
Use reflection to get information about a class
You define a FatherClass class and then a SonClass that inherits from the FatherClass class, as shown below.
FatherClass.java
public class FatherClass {
public String fatherName;
public int fatherAge;
public void printFatherMsg(){}
}
Copy the code
SonClass.java
public class SonClass extends FatherClass{ private String sonName; protected int sonAge; public String sonBirthday; public void printSonMsg(){ System.out.println("Son Msg - name : " + sonName + "; age : " + sonAge); } private void setSonName(String name){ sonName = name; } private void setSonAge(int age){ sonAge = age; } private int getSonAge(){ return sonAge; } private String getSonName(){ return sonName; }}Copy the code
1. Obtain information about all variables of the class
Public static void main(String[] args) {//1. Class sonClass = sonclass.class; Println (" class name: "+ sonClass.getName()); System.out.println("======================================"); Field[] fields = sonclass.getFields (); sonclass.getFields (); / / 2.2 for all the class declaration of variables (don't ask, don't access) Field [] fields2 = sonClass. GetDeclaredFields (); System.out.println("getFields() get variable: "); goThroughFields(fields); System.out.println("======================================"); System.out.println("getDeclaredFields() get variable: "); goThroughFields(fields2); } private static void goThroughFields(Field[] fields) { for (Field field : Fields) {// Get access and print int modiFIERS = fiel.getmodiFIERS (); System.out.print(Modifier.toString(modifiers) + " "); System.out.println(field.getType().getName()+ "" + field.getName()); }}Copy the code
The above code comments are very detailed and will not be explained. Note the difference between getFields() in 2.1 and getDeclaredFields() in 2.2 in the comment. Let’s look at the output in both cases. SonClass extends FatherClass extends Object:
-
The getFields() method is called to output the public methods of the SonClass class and its inherited parents, including FatherClass and Object. Note: The Object class has no member variables, so there is no output.
public java.lang.String sonBirthday public java.lang.String fatherName public int fatherAge Copy the code
-
Call getDeclaredFields() to print all member variables of the SonClass, regardless of access rights.
private java.lang.String sonName protected int sonAge public java.lang.String sonBirthday Copy the code
2. Get all method information for the class
Public static void main(String[] args) {//1. Class mClass = sonclass.class; System.out.println(" class name: "+ McLass.getname ()); System.out.println("======================================"); [] methods = McLass.getmethods (); / / 2.2 for all this kind of Method (don't ask, don't access) Method [] methods2. = mClass getDeclaredMethods (); System.out.println(" [getMethods()] "); goThroughMethods(methods); System.out.println("======================================"); System.out.println(" [getDeclaredMethods()] "); goThroughMethods(methods2); } private static void goThroughMethods(Method[] methods) { for (Method method : Methods) {// Get and print access to methods (Modifiers) int Modifiers = method.getmodifiers (); System.out.print(Modifier.toString(modifiers) + " "); Class returnType = method.getreturnType (); System.out.print(returnType.getName() + " " + method.getName() + "("); Parameter[] parameters = method.getparameters (); for (Parameter parameter: parameters) { System.out.print(parameter.getType().getName() + " " + parameter.getName() + ","); Class[] exceptionTypes = method.getExceptionTypes(); Class[] exceptionTypes = method.getExceptionTypes(); if (exceptionTypes.length == 0){ System.out.println(")"); } else { for (Class c : exceptionTypes) { System.out.println(") throws " + c.getName()); }}}}Copy the code
As with variable information, note the difference between 2.1 and 2.2 in the comments. Here’s the printout:
-
Call getMethods() to get all public access rights to SonClass methods, including those inherited from the parent class. In the printed message, the printSonMsg() method comes from the SonClass class, printFatherMsg() from the FatherClass class, and the rest from the Object class.
public void printSonMsg( ) public void printFatherMsg( ) public final void wait( ) throws java.lang.InterruptedException public final void wait( long arg0,int arg1, ) throws java.lang.InterruptedException public final native void wait( long arg0, ) throws java.lang.InterruptedException public boolean equals( java.lang.Object arg0, ) public java.lang.String toString( ) public native int hashCode( ) public final native java.lang.Class getClass( ) public final native void notify( ) public final native void notifyAll( )Copy the code
-
Call the getDeclaredMethods() method
In the printed information, all SonClass methods are printed, regardless of access rights.
Class name: obj.SonClass private int getSonAge( ) private void setSonAge( int arg0, ) public void printSonMsg( ) private void setSonName( java.lang.String arg0, ) private java.lang.String getSonName( )Copy the code
Public Method getMethod(String name,Class<? >… ParameterTypes) throws NoSuchMethodException,SecurityException; Call method –> Public Object invoke(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException; # # 4. To achieve specified members: public Filed getDeclaredFiled (String name) throws NoSuchMethodException, SecurityException.
Access or manipulate class private variables and methods
Above, we successfully obtained the class variable and method information, confirming the idea of obtaining information dynamically at run time. So, just getting information?
It is not possible to access or manipulate class private variables and methods through objects, but reflection does. How to use reflection to access private methods of class objects and modify private variables or constants.
TestClass.java
public class TestClass { private String MSG = "Original"; private void privateMethod(String head , int tail){ System.out.print(head + tail); } public String getMsg(){ return MSG; } //String is optimized by JVM private final String FINAL_VALUE = "final "; public String getFinalValue(){ return FINAL_VALUE; }}Copy the code
1. Access private methods
To access a privateMethod in the TestClass class privateMethod(…) As an example
Private static void main(String[] args) throws Exception{//1. TestClass TestClass = new TestClass(); Class mClass = testClass.getClass(); // The first parameter is the name of the private method to be obtained. // The second parameter is the type of the parameter to be obtained. The parameter is Class... // Method arguments can also be written like this: new Class[]{String.class , int.class} Method privateMethod = mClass.getDeclaredMethod("privateMethod", String.class, int.class); If (privateMethod! = null) {/ / get access to the private methods / / just get access to, and not change the actual permissions privateMethod. SetAccessible (true); // invoke a privateMethod with the invoke reflection //privateMethod is the privateMethod that testClass operates on //privateMethod is the object that testClass operates on //privateMethod. "Java Reflect Test", 123); }}Copy the code
The setAccessible(true) method in step 3 is used to obtain access to a private method. If not, IllegalAccessException is reported.
Exception in thread "main" java.lang.IllegalAccessException: Class com.yxm.demo.demo2.Test3 can not access a member of class com.yxm.demo.demo2.TestClass with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296) at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288) at java.lang.reflect.Method.invoke(Method.java:491) at com.yxm.demo.demo2.Test3.main(Test3.java:29)Copy the code
The private method was called successfully:
Java Reflect Test 123
Copy the code
2. Modify private variables
Take the example of modifying the private MSG variable in the TestClass class. Its initial value is “Original”, and we want to modify it to “Modified”. Same old rule: go to the code first and read the comments.
Private static void main(String[] args) throws Exception {//1. TestClass TestClass = new TestClass(); Class mClass = testClass.getClass(); Field privateField = McLass.getdeclaredfield ("MSG"); //3. Manipulate the private variable if (privateField! = null) {/ / get access to the private variable privateField. SetAccessible (true); Println ("Before Modify: MSG = "+ testClass.getmsg ()); // Modify private variables and output to test system.out.println ("Before Modify: MSG =" + testClass.getmsg ()); // Call set(object, Set (testClass, "Modified"); privateField (testClass, "Modified"); System.out.println("After: MSG = "+ testclass.getmsg ()); }}Copy the code
This code is similar to the logic used to access the private method, so it will not be repeated.
Before Modify: MSG = Original After Modify: MSG = ModifiedCopy the code
3. Modify private constants
In 3.2, we introduced how to modify private variables. Now let’s talk about how to modify private constants.
A constant is a member property that is modified with a final modifier. It is distinguished from a variable by the presence or absence of the final keyword modifier. Before I get to that, let me add one more point.
The Java Virtual Machine (JVM) optimizes our code for efficiency when compiling.java files to produce.class files. One of these optimizations is that the JVM replaces references to constants with specific constants at compile time, as shown below.
.java files before compilation:
Private final String FINAL_VALUE = "hello"; if(FINAL_VALUE.equals("world")){ //do something }Copy the code
The resulting.class file is compiled (without comments, of course) :
private final String FINAL_VALUE = "hello"; // Replace "hello" if("hello".equals("world")){//do something}Copy the code
However, not all constants are optimized. When tested, the JVM optimizes for basic types such as int, Long, Boolean, and String, but not for wrapper types such as Integer, Long, and Boolean, or other types such as Date and Object.
To summarize: For static constants of primitive types, the JVM replaces the code that references this constant with a specific constant value at compile time.
We can still use reflection to change the value of a constant at runtime (verified later), but the JVM gets.class files at compile time that optimize constants to specific values and use them directly at runtime, so changing the value of a constant is meaningless.
Public static void main(String[] args) throws Exception {//1. TestClass TestClass = new TestClass(); Class mClass = testClass.getClass(); Field finalField = McLass.getdeclaredfield ("FINAL_VALUE"); If (finalField! Finalfield.setaccessible (true); = null) {// Obtain access to private constants finalField.setaccessible (true); System.out.println("Before Modify: FINAL_VALUE = " + finalField.get(testClass)); Finalfield. set(testClass, "Modified"); Println ("After Modify: FINAL_VALUE = " + finalField.get(testClass)); Println ("Actually: FINAL_VALUE = "+ testClass.getFinalValue()); }}Copy the code
Output:
Before Modify: FINAL_VALUE = FINAL After Modify: FINAL_VALUE = Modified Actually: FINAL_VALUE = FINALCopy the code
The results are in:
Print the value of FINAL_VALUE before modification. There is no objection.
The value of FINAL_VALUE is changed by reflection.
The third sentence prints the value of FINAL_VALUE obtained by the getFinalValue() method, but it is still the initial value, resulting in invalid changes
Testclass. Java: testclass. Java: testclass. Java: testclass. Java: testclass. Java
GetFinalValue () returns “FINAL”
Java reflection library
The reflection provided by Java native is cumbersome and not inconvenient to use. For example, if we want to call the static method get of UserManager, the native implementation is as follows
Method setMethod = SonClass.class.getDeclaredMethod("setSonName", String.class); setMethod.setAccessible(true); Method getMethod = SonClass.class.getDeclaredMethod("getSonName"); getMethod.setAccessible(true); Setmethod.invoke (obj, "Zhang SAN "); System.out.println(getMethod.invoke(obj));Copy the code
It’s easy to implement. the
- You need to determine the Method name and parameters to get the corresponding Method object
- Set the Method object’s assessible to true
- Call the invoke method and pass in the corresponding argument
- Catch a string of exceptions that might be thrown
Could reflection be easier? Sure, it would be a lot easier.
This is what I want to introduce in this article, jOOR(Java Object Oriented Reflection), which is a simple encapsulation of the Java.lang.Reflect package that makes it more straightforward and convenient to use.
With jOOR, the above code can be shortened to one line.
Reflect.on(obj). Call ("setSonName", "lisi "); System.out.println(Reflect.on(obj).call("getSonName"));Copy the code
Rely on
- JOOR has no dependencies.
- To use jOOR, you just need to add these two files (reflectException. Java and reflectException. Java) to the project.
The API is introduced
Reflect
- Reflect.on wraps a Class or object to Reflect on. The value of the Class can be Class or the full Class name (including the package name information).
- Reflect.create is used to call the constructor of the previous class, with two overloads, one with and one without arguments
- Reflect.call method call, passing in the method name and parameters, and calling GET if there is a return value
- Reflect.get gets (field and method return) values and performs type conversions. It is often used in combination with call and field
- Reflect.field gets the value associated with the property, which requires a call to get
- Reflect.set Sets attribute correlation.
ReflectException
Introducing reflectExceptions prevents us from catching too many exceptions and reduces the amount of vertical code, making the code much cleaner. ReflectException is thrown, and the following exception may have occurred.
- ClassNotFoundException
- IllegalAccessException
- IllegalArgumentException
- InstantiationException
- InvocationTargetException
- NoSuchMethodException
- NoSuchFieldException
- SecurityException
In addition, a ReflectException is an unchecked exception. It is grammatically unnecessary to explicitly catch an exception, but you need to consider whether to explicitly catch an exception based on the actual situation.
Use the sample
Create an instance
String string = Reflect.on(String.class).create("Hello World").get();
Copy the code
Access attributes (public, protected, package, private)
char pathSeparatorChar = Reflect.on(File.class).create("/sdcard/droidyue.com").field("pathSeparatorChar").get();
Copy the code
Modify properties (Final properties can also be modified)
String setValue = Reflect.on(File.class).create("/sdcard/drodiyue.com").set("path", "fakepath").get("path");
Copy the code
Calls the method (public, protected, package, private)
ArrayList arrayList = new ArrayList();
arrayList.add("Hello");
arrayList.add("World");
int value = Reflect.on(arrayList).call("hugeCapacity", 12).get();
Copy the code
Realize the principle of
Reflect is essentially a wrapper around native Java Reflect, masking extraneous details.
In the case of the Fields method, the internal implementation can be seen as calling the reflection-related code provided by Java natively.
public Map<String, Reflect> fields() { Map<String, Reflect> result = new LinkedHashMap<String, Reflect>(); Class<? > t = type(); do { for (Field field : t.getDeclaredFields()) { if (type ! = object ^ Modifier.isStatic(field.getModifiers())) { String name = field.getName(); if (! result.containsKey(name)) result.put(name, field(name)); } } t = t.getSuperclass(); } while (t ! = null); return result; }Copy the code