Detailed explanation of Java reflection mechanism (from simple to deep, you don't know the super detailed JVM memory structure

Posted by MiCR0 on Wed, 15 Dec 2021 20:40:04 +0100

```

Name of class: 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(  )

```

3, Accessing or manipulating private variables and methods of a class

================

In the above, we successfully obtained the variable and method information of the class, and verified the view of "dynamically obtaining information" at run time. So, just getting information? Let's look back.

As we all know, objects cannot access or manipulate private variables and methods of classes, but we can do it through reflection. Yes, reflection can do it! Next, let's explore how to use reflection to access the private methods of the , class object and modify , private variables or constants.

The old rule is to go to the test class first.

Note:

  1. Please pay attention to the modifiers (access rights) of variables and methods in the test class;

  2. The test class is only for testing. It is not recommended to write this in actual development:)

TestClass.java

3.1 access private methods

To access the private method in the {TestClass} class} privateMethod(...) For example, adding parameters to the method is to consider the most complete situation. It's very considerate? First post the code and look at the comments. Finally, I will focus on explaining some of the code.

/**

 * Private methods for accessing objects

 * For the sake of concise code, throw general exceptions on methods. Don't do this in actual development

 */

private static void getPrivateMethod() throws Exception{

    //1. Get Class instance

    TestClass testClass = new TestClass();

    Class mClass = testClass.getClass();



    //2. Get private method

    //The first parameter is the name of the private method to get

    //The second is the type of the parameter of the method to be obtained. The parameter is Class, No parameter is null

    //Method parameters can also be written like this: 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

        //It is only to obtain access, not to modify the actual permissions

        privateMethod.setAccessible(true);



        //Calling private methods using invoke reflection

        //privateMethod is the obtained private method

        //testClass object to operate on

        //The last two parameters are passed as arguments

        privateMethod.invoke(testClass, "Java Reflect ", 666);

    }

}

It should be noted that the {setAccessible(true) method in step 3 is to obtain the access permission of the private method. If it is not added, an exception} IllegalAccessException will be reported, because the access permission of the current method is "private", as follows:

java.lang.IllegalAccessException: Class MainClass can not access a member of class obj.TestClass with modifiers "private"

After normal operation, print the following and call the private method successfully:

Java Reflect 666

3.2 modifying private variables

Take modifying the private variable "MSG" in "TestClass" as an example. Its initial value is "Original" and we want to modify it to "Modified". The old rule is to read the code and comments first.

/**

 * Modify the value of the object's private variable

 * To simplify the code, throw a total exception on the method

 */

private static void modifyPrivateFiled() throws Exception {

    //1. Get Class instance

    TestClass testClass = new TestClass();

    Class mClass = testClass.getClass();



    //2. Get private variables

    Field privateField = mClass.getDeclaredField("MSG");



    //3. Operate private variables

    if (privateField != null) {

        //Get access to private variables

        privateField.setAccessible(true);



        //Modify private variables and output to test

        System.out.println("Before Modify: MSG = " + testClass.getMsg());



        //Call set (object, value) to modify the value of the variable

        //privateField is the obtained private variable

        //testClass object to operate on

        //"Modified" is the value to be modified to

        privateField.set(testClass, "Modified");

        System.out.println("After Modify: MSG = " + testClass.getMsg());

    }

}

The code here is similar to the logic of accessing private methods, so I won't repeat it. From the output information, it can be seen that "modifying private variables" is successful:

Before Modify: MSG = Original

After Modify: MSG = Modified

3.3 modifying private constants

In 3.2, we introduced how to modify the private} variable. Now let's talk about how to modify the private} constant,

01. Can you really modify it?

Constants refer to member attributes modified with the {final} modifier. The difference between constants and variables is whether they are modified with the} final} keyword. Before speaking, add a knowledge point.

The java virtual machine (JVM) will optimize our code to improve efficiency when compiling. java  files to obtain. class  files. One of the optimizations is that the JVM will replace the code referencing constants with specific constant values at the compilation stage, as shown below (part of the code).

Precompiled java file:

//Note that it is a value of type String

private final String FINAL_VALUE = "hello";



if(FINAL_VALUE.equals("world")){

    //do something

}

After compilation class} file (of course, there are no comments after compilation):

private final String FINAL_VALUE = "hello";

//Replace with "hello"

if("hello".equals("world")){

    //do something

}

However, not all constants are optimized. After testing, the JVM will be optimized for basic types such as int, long, Boolean, and String, but will not be optimized for wrapper types such as Integer, long, Boolean, or other types such as Date and Object.

To sum up: for static constants of basic types, the JVM will replace the code referencing this constant with a specific constant value at the compilation stage.

So, in actual development, if we want to modify the constant value of a class, which happens to be a basic type, isn't there nothing we can do? Anyway, I personally think I can't help it unless I modify the source code!

The so-called powerlessness here means that we can still use reflection to modify the constant value at the running time of the program (code verification will be carried out later), but the. class file obtained by the JVM at the compilation stage has optimized the constant to a specific value, and the specific value is directly used at the running stage, so even if the constant value is modified, it is meaningless.

Let's verify this and add the following code to the test class TestClass:

Next, modify the constant value. First, the code. Please read the notes carefully:

/**

 * Modify the value of the object's private constant

 * For the sake of concise code, throw general exceptions on methods. Don't do this in actual development

 */

private static void modifyFinalFiled() throws Exception {

    //1. Get Class instance

    TestClass testClass = new TestClass();

    Class mClass = testClass.getClass();



    //2. Get private constant

    Field finalField = mClass.getDeclaredField("FINAL_VALUE");



    //3. Modify the value of constant

    if (finalField != null) {



        //Get access to private constants

        finalField.setAccessible(true);



        //Call the getter method of finalField

        //Output FINAL_VALUE value before modification

        System.out.println("Before Modify: FINAL_VALUE = "

                + finalField.get(testClass));



        //Modify private constants

        finalField.set(testClass, "Modified");



        //Call the getter method of finalField

        //Output FINAL_VALUE modified value

        System.out.println("After Modify: FINAL_VALUE = "

                + finalField.get(testClass));



        //Use the object to call the getter method of the class

        //Get value and output

        System.out.println("Actually : FINAL_VALUE = "

                + testClass.getFinalValue());

    }

}

The above code is not explained. The comments are huge and detailed! Pay special attention to the notes in step 3, and then take a look at the output. I can't wait to polish my eyes:

Before Modify: FINAL_VALUE = FINAL

After Modify: FINAL_VALUE = Modified

Actually : FINAL_VALUE = FINAL

The results came out:

The first sentence is printed before modification_ Value , no objection;

The second sentence prints the value of the modified constant, indicating FINAL_VALUE is indeed modified by reflection;

The third sentence prints the {final obtained by the} getFinalValue() method_ Value , but it is still the initial value, resulting in invalid modification!

Do you think the result is credible? What, don't you believe it? How do I know that the JVM will optimize the code after compilation? How about this? Let's take a look at testclass Java} file compiled testclass Class file. In order to avoid saying that the code was written by myself, I decided not to paste the code and directly screenshot:

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-mbwbpm76-163082611276)( https://user-gold-cdn.xitu.io/2018/11/9/166f6b7bbe04c16d?w=1015&h=938&f=png&s=390390 )]

TestClass.class file

See, there is a picture and a truth. The getFinalValue() method directly returns "final"! It also shows that the program runtime is based on the compiled class.

By the way, if you have time, you can try changing several data types. As mentioned above, some data types will not be optimized. You can modify the data type and try it according to my ideas. If you think the output is unreliable, just look at it directly Classes} file, you can see which data types are optimized and which are not optimized at a glance. Let's talk about the next knowledge point.

02. Try to modify it!

You can't change it. Can you bear it? Don't worry. I don't know if you found out. The constants just now are directly assigned when they are declared. You may wonder, aren't constants always assigned at the time of declaration? No assignment, no error? Of course not.

Method 1

In fact, Java allows us to declare constants without assignment, but we must assign values in the constructor. You may ask me why I said this, which explains:

Let's modify the {TestClass} class, do not assign a value when declaring a constant, and then add a constructor and assign a value to it. Take a look at the modified code (part of the code):

Now, we call the method of modifying constants posted above and find that the output is as follows:

Before Modify: FINAL_VALUE = FINAL

After Modify: FINAL_VALUE = Modified

Actually : FINAL_VALUE = Modified

Nani, did you output the modified value in the last sentence? Yes, the modification succeeded! If you want to know why, you have to look at the compiled testclass Class file, marked in the figure.

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ti3v8j3j-163082611277)( https://user-gold-cdn.xitu.io/2018/11/9/166f6b9021cfc7b3?w=1280&h=844&f=png&s=503454 )]

TestClass.class file

Explain: we put the assignment in the constructor, which is only called by the new object when we run. Therefore, instead of directly assigning a value to a constant, we will optimize the getFinalValue() method to return a constant value at the compilation stage, but point to the getFinalValue() method_ Value, so it makes sense for us to modify the bright value through reflection at run time. However, it can be seen that the program is still optimized by optimizing the assignment statement in the constructor. Think about that sentence again. The program runs according to the compiled class to execute, I believe you must understand why it is so output!

Method 2

Please make sure you clear the top before you look down. Next, let's talk about another modification. You can successfully modify the value of a constant without using a constructor, but the principle is the same. Remove the constructor and change the statement declaring the constant to use the ternary expression for assignment:

private final String FINAL_VALUE

        = null == null ? "FINAL" : null;

In fact, the above code is equivalent to "FINAL" directly_ Value , is assigned "FINAL", but it can! As for why, you think so: null = = null? "FINAL": null is calculated at runtime. It will not be calculated at compile time and will not be optimized, so you know.

To sum up, no matter using constructors or ternary expressions, they basically avoid being optimized at compile time, so that it makes sense after we modify constants through reflection! Well, that's the end of this small part!

Final emphasis:

You must be reminded that we can actually successfully modify the value of a constant through reflection, whether we directly assign a value to a constant, assign a value to a constant through a constructor, or {use a ternary operator. The "success" of the modification I mentioned above means that we can certainly modify the constant value through reflection during the program running stage, but the actual implementation of the optimized value class file, does the modified value really work? In other words, does the compiler replace constants with specific values? If it is replaced, how to modify the constant value will not affect the final result, will it?.

In fact, you can think directly: reflection can certainly modify the value of constants, but does the modified value make sense?

03. Can we change it?

Can we change it? In other words, is there any meaning after reflection modification?

If you see above, the answer will be simple. As the saying goes, "a thousand words is not as good as a picture". Let me use a non-standard flow chart to express the answer directly.

Note: "cannot modify" in the figure can be understood as "can modify the value but has no meaning" Modifiable means that the value can be modified and meaningful.

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-ps3miff7-163082611279)( https://user-gold-cdn.xitu.io/2017/8/12/2bdcae6d3cde4638c23a0cfe34f85aa6 )]

last

If you need this redis video, interview questions and technical documents sorted out by Tsinghua Daniel, please praise it

CodeChina open source project: [analysis of Java interview questions of front-line large manufacturers + core summary learning notes + latest explanation Video]

I wish everyone to enter the big factory as soon as possible and get a satisfactory salary and rank ~ ~ ~ come on!!

Thank you for your support!!

In fact, you can think directly: reflection can certainly modify the value of constant, but does the modified value make sense?

03. Can we change it?

Can we change it? In other words, is there any meaning after reflection modification?

If you see above, the answer will be simple. As the saying goes, "a thousand words is not as good as a picture". Let me use a non-standard flow chart to express the answer directly.

Note: "cannot modify" in the figure can be understood as "can modify the value but has no meaning" Modifiable means that the value can be modified and meaningful.

[external chain pictures are being transferred... (img-ps3miff7-163082611279)]

last

If you need this redis video, interview questions and technical documents sorted out by Tsinghua Daniel, please praise it

CodeChina open source project: [analysis of Java interview questions of front-line large manufacturers + core summary learning notes + latest explanation Video]

I wish everyone to enter the big factory as soon as possible and get a satisfactory salary and rank ~ ~ ~ come on!!

Thank you for your support!!

[external chain picture transferring... (img-ylppfpvb-163082611281)]

Topics: Java Back-end Programmer