This post reflects a little bit about Learning About Reflection in Java. Before you learn about Reflection, you should learn about the Class classes in Java, or if you don’t know much about them, take a quick look at them.
Java reflection mechanism
Referred to many blog posts, summed up the following personal views, if there is anything wrong also hope to correct:
Java reflection allows you to know all the properties and methods of any class at 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.
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.
Do not know the above theory you can understand, anyway just contact reflection when I a face meng ratio, later wrote a few examples: oh ~~ originally is this meaning!
It doesn’t matter if you don’t understand the theory at the moment, just read the examples and come back and I’m sure you’ll understand.
Use reflection to get information about a class
To make the test results more obvious, I first define a FatherClass class that inherits from the Object class by default, and then a SonClass that inherits from the FatherClass class, as shown below. As you can see, the access permissions for variables and methods in the test class are not very formal. They are deliberately set to make it easier to see the test results.
FatherClass.java
public class FatherClass {
public String mFatherName;
public int mFatherAge;
public void printFatherMsg(a){}}Copy the code
SonClass.java
public class SonClass extends FatherClass{
private String mSonName;
protected int mSonAge;
public String mSonBirthday;
public void printSonMsg(a){
System.out.println("Son Msg - name : "
+ mSonName + "; age : " + mSonAge);
}
private void setSonName(String name){
mSonName = name;
}
private void setSonAge(int age){
mSonAge = age;
}
private int getSonAge(a){
return mSonAge;
}
private String getSonName(a){
returnmSonName; }}Copy the code
1. Obtain information about all variables of the class
/** * Get all variables of the class by reflection */
private static void printFields(a){
//1. Get and print the name of the class
Class mClass = SonClass.class;
System.out.println("Class name:" + mClass.getName());
//2.1 Get all public access variables
// both declared by this class and inherited from its parent class
Field[] fields = mClass.getFields();
//2.2 Get all variables declared in this class (without permission)
//Field[] fields = mClass.getDeclaredFields();
//3. Iterate over variables and output variable information
for (Field field :
fields) {
// Get access and output
int modifiers = field.getModifiers();
System.out.print(Modifier.toString(modifiers) + "");
// Output the type and name of the variable
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.
Obj. SonClass public java.lang.String mSonBirthday public java.lang.String mFatherName public int mFatherAge obj.SonClass public java.lang.String mSonBirthday public java.langCopy the code
-
Call getDeclaredFields() to print all member variables of the SonClass, regardless of access rights.
Obj. SonClass private java.lang.String mSonName protected int mSonAge public java.lang.String mSonBirthdayCopy the code
2. Get all method information for the class
/** * Get all methods of the class */ by reflection
private static void printMethods(a){
//1. Get and print the name of the class
Class mClass = SonClass.class;
System.out.println("Class name:" + mClass.getName());
//2.1 Method for obtaining all public access permissions
// include declarations and inherits from the parent class
Method[] mMethods = mClass.getMethods();
// get all methods of this class (without access permission)
//Method[] mMethods = mClass.getDeclaredMethods();
//3. Iterate through all methods
for (Method method :
mMethods) {
// Get and output method access permissions
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers) + "");
// Gets and prints the return value type of the method
Class returnType = method.getReturnType();
System.out.print(returnType.getName() + ""
+ method.getName() + "(");
// Get and print all the parameters of the method
Parameter[] parameters = method.getParameters();
for (Parameter parameter:
parameters) {
System.out.print(parameter.getType().getName()
+ "" + parameter.getName() + ",");
}
// Get and print the exception thrown by the method
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.
Class name: obj.SonClass public voidprintSonMsg( ) 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.
Obj. SonClass private int getSonAge() private void obj.SonClass private int getSonAge() private void obj.SonClass private int getSonAge() private void objsetSonAge( int arg0, ) public void printSonMsg( ) private void setSonName( java.lang.String arg0, ) private java.lang.String getSonName( ) Copy the code
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? Let’s look back.
We all know that objects cannot access or manipulate the private variables and methods of a class, but reflection allows us to do so. Yes, reflection can do it! Let’s explore how reflection can be used to access private methods of class objects and modify private variables or constants.
Same old rule. Test class first.
Note:
- Notice the modifiers (access permissions) for variables and methods in the test class;
- The test class is for testing only. It is not recommended to write this in actual development:
TestClass.java
public class TestClass {
private String MSG = "Original";
private void privateMethod(String head , int tail){
System.out.print(head + tail);
}
public String getMsg(a){
returnMSG; }}Copy the code
3.1 Accessing private methods
To access a privateMethod in the TestClass class privateMethod(…) For example, the method adds parameters to consider the most complete case, which is nice, isn’t it? Post the code first, read the comments, and finally I’ll highlight some of the code.
/** * Private methods that access objects * are concise code that throws total exceptions on methods
private static void getPrivateMethod(a) throws Exception{
//1. Get the Class instance
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. Get the private method
// The first argument is the name of the private method to get
// The second is the type of the parameter to get the method. The parameter is Class... Null if there is no argument
New Class[]{String. Class, int. Class}
Method privateMethod =
mClass.getDeclaredMethod("privateMethod", String.class, int.class);
//3. Start operation method
if(privateMethod ! =null) {
// Get access to private methods
// Get access, not change actual permissions
privateMethod.setAccessible(true);
// Use the invoke reflection to call a private method
//privateMethod is the obtained privateMethod
//testClass specifies the object to operate on
// Pass the last two arguments as arguments
privateMethod.invoke(testClass, "Java Reflect ".Awesome!); }}Copy the code
The setAccessible(true) method in step 3 is used to obtain access to a private method. If not, IllegalAccessException is reported.
java.lang.IllegalAccessException: Class MainClass can not access a member of class obj.TestClass with modifiers "private"
Copy the code
The private method was called successfully:
Java Reflect 666
Copy the code
3.2 Modifying 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.
/** * Modifies the value of an object's private variable * to clean code, throwing a total exception on the method */
private static void modifyPrivateFiled(a) throws Exception {
//1. Get the Class instance
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. Get private variables
Field privateField = mClass.getDeclaredField("MSG");
//3. Operate on private variables
if(privateField ! =null) {
// Get access to private variables
privateField.setAccessible(true);
// Modify private variables and output them to test
System.out.println("Before Modify:MSG = " + testClass.getMsg());
// Call set(object, value) to change the value of a variable
//privateField is the obtained private variable
//testClass specifies the object to operate on
//"Modified" indicates the value to be Modified
privateField.set(testClass, "Modified");
System.out.println("After Modify: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.3 Modifying Private Constants
In 3.2, we introduced how to modify private variables. Now let’s talk about how to modify private constants.
01. Can it really be modified?
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 (part of the code).
.java files before compilation:
// Note that this is a String value
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 with "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.
So in practice, if we want to change the value of a class constant that happens to be of a primitive type, there’s nothing we can do about it. Anyway, I personally think unless modify the source code, otherwise really no way!
So-called powerless here refers to: we still can use in programs run time reflection modified constant value (later code validation), but the JVM in compilation phase. The class file will have the value of the constant optimization of concrete directly using the exact value in operation stage, so even changed the constant value has no meaning.
Let’s verify this by adding the following code to the TestClass class:
//String is optimized by the JVM
private final String FINAL_VALUE = "FINAL";
public String getFinalValue(a){
// It will be optimized to return "FINAL"
return FINAL_VALUE;
}
Copy the code
Next, modify the constant value, first code, please read the comment carefully:
/** * change the value of the object's private constant * to concise code, throw a total exception on the method, real development do not do this */
private static void modifyFinalFiled(a) throws Exception {
//1. Get the Class instance
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. Get private constants
Field finalField = mClass.getDeclaredField("FINAL_VALUE");
//3. Change the constant value
if(finalField ! =null) {
// Get access to private constants
finalField.setAccessible(true);
// Call finalField's getter
// Output the value of FINAL_VALUE before modification
System.out.println("Before Modify:FINAL_VALUE = "
+ finalField.get(testClass));
// Modify private constants
finalField.set(testClass, "Modified");
// Call finalField's getter
// Output the modified FINAL_VALUE value
System.out.println("After Modify:FINAL_VALUE = "
+ finalField.get(testClass));
// Call the class's getter method with the object
// Get the value and print it
System.out.println("Actually: FINAL_VALUE ="+ testClass.getFinalValue()); }}Copy the code
The above code does not explain, there is a huge detailed comment! Pay special attention to the comment for step 3, and then take a look at the 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 value of FINAL_VALUE obtained by getFinalValue() is still the initial value.
Do you find that credible? What, you still don’t believe me? How do I know that the JVM will optimize the code after compilation? The testClass.class file was compiled from the testClass.java file. To avoid saying I wrote the code myself, I decided not to paste the code and take a screenshot:
GetFinalValue () returns “FINAL”! It also shows that the program is executed according to the compiled.class.
By the way, if you have time, you can try a few different data types. As mentioned above, some data types are not optimized. You can modify the datatype and try it out. If you think the output is unreliable, just look at the.classs file. You can see at a glance which datatype is optimized and which datatype is not. Let’s move on to the next point.
02. Try to fix it!
You can’t change it. You can live with that? Don’t worry, I don’t know if you noticed that all the constants are assigned directly at declaration time. You might wonder, aren’t constants always assigned at declaration time? No assignment, no error? Of course not.
Methods a
In fact, Java allows us to declare constants without assigning them, but we must assign them in constructors. You may ask me why I say this, which explains:
Let’s modify the TestClass class to declare constants without assigning, and then add a constructor and assign to it. Look at the modified code (partial code) :
public class TestClass {
/ /...
private final String FINAL_VALUE;
// Assign a constant inside the constructor
public TestClass(a){
this.FINAL_VALUE = "FINAL";
}
/ /...
}
Copy the code
Now we call the method posted above to modify the constant and find the output looks like this:
Before Modify: FINAL_VALUE = FINAL After Modify: FINAL_VALUE = Modified Actually: FINAL_VALUE = ModifiedCopy the code
Nani, does the last sentence print the modified value? Yes, it worked! To see why, look at the map of the compiled testclass. class file.
Explain: Instead of optimizing the getFinalValue() method at compile time to return a constant value, as we did when assigning constants directly, we’ll point to FINAL_VALUE, This makes sense when we modify the brightness values through reflection at run time. However, you can see that the program has been optimized to optimize the assignment statement in the constructor. The program is executed from a compiled.class, and you can see why.
Method 2
Please make sure you clear the top before you go down. Here’s another change. It’s possible to successfully change the value of a constant without using a constructor, but the principle is the same. Remove the constructor and change the statement declaring a constant to use a ternary expression assignment:
private final String FINAL_VALUE
= null= =null ? "FINAL" : null;
Copy the code
In fact, the above code is equivalent to assigning “FINAL” to FINAL_VALUE directly, but it can! Why, you think: NULL == null? “FINAL” : NULL is computed at run time, not computed at compile time, and therefore not optimized, so you get the idea.
In summary, both constructors and ternary expressions are basically used to avoid optimization at compile time so that it makes sense to modify constants through reflection! Well, that’s the end of this little segment!
Final emphasis:
It is important to remind you that we can actually modify the value of a constant successfully by reflection, whether we assign a constant directly, via a constructor, or by using the ternary operator. By “successful”, I mean that we can certainly change constant values at runtime through reflection, but when we actually execute the optimized.class file, does the change really make a difference? In other words, are constants replaced with specific values at compile time? If you do, no amount of changing the value of the constant will affect the final result, right? .
In fact, you can think of it this way: reflection can certainly change the value of a constant, but does the change make sense?
03. Can it be changed or not?
Can it be changed or not? In other words, does reflection make sense after modification?
If you read the above, the answer is easy. As the saying goes, “a thousand words are worth more than a picture”, let me use a less formal flow chart to directly express the answer.
Note: “cannot modify” in the figure can be interpreted as “can modify value but has no meaning”; Modifiable means modifiable and meaningful.
Four,
Well, this record is here, suddenly found that write a lot unconsciously, thank you for listening to me. I think this blog if you read carefully, there will be harvest! Finally, because the content is more, more knowledge, if there is any mistake or improper place, but also hope to correct. Welcome message exchange!
Scan the qr code below, follow my official account, timely access to the latest article push!