Can final modified variables be modified by reflection

First of all, let's study String. In Java, String is expressed as an immutable string. Can the value of ordinary String be modified?

import java.lang.reflect.Field;

public class FinalTest {
    public static void main(String[] args) {
        String str = "ABCD";
        System.out.println("str = " + str);
        System.out.println("hashCode = " + str.hashCode());
        try {
            Field valueField = String.class.getDeclaredField("value");
            //value域是final private的,这里设置可访问
            valueField.setAccessible(true);
            char[] valueCharArr = (char[]) valueField.get(str);
            valueCharArr[0] = 'G';
            //此处输出第一组结果
            System.out.println("str = " + str);
            System.out.println("hashCode = " + str.hashCode());
            valueField.set(str, new char[] {'1', '2'});
            //此处输出第二组结果
            System.out.println("str = " + str);
            System.out.println("hashCode = " + str.hashCode());
        } catch (NoSuchFieldException | SecurityException
                | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

Output result:

It can be seen that although String is immutable, immutable means that once the String is created, you cannot modify its value, it always looks like that in memory, because all the member variables in the source code of String are Private modification, and does not provide any method to modify its value, everyone who is interested can take a look at the source code of String.

But we can use other methods, that is, reflection to modify its attribute value. In the above code, we clearly see that the value of String can be modified, but this does not mean that String is variable, because our value The property stores a reference to char [], we can change the value of this object, but you can never modify the address of this reference.

But if we add final to this String, what will happen?

import java.lang.reflect.Field;

public class FinalTest {
    public static void main(String[] args) {
        final String str = "ABCD";
        System.out.println("str = " + str);
        System.out.println("hashCode = " + str.hashCode());
        try {
            Field valueField = String.class.getDeclaredField("value");
            //value域是final private的,这里设置可访问
            valueField.setAccessible(true);
            char[] valueCharArr = (char[]) valueField.get(str);
            valueCharArr[0] = 'G';
            //此处输出第一组结果
            System.out.println("str = " + str);
            System.out.println("hashCode = " + str.hashCode());
            valueField.set(str, new char[] {'1', '2'});
            //此处输出第二组结果
            System.out.println("str = " + str);
            System.out.println("hashCode = " + str.hashCode());
        } catch (NoSuchFieldException | SecurityException
                | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

result:

We found that why the value cannot be changed again, is the reflection invalid?

Then we change the output method of String to call toString () and see:

import java.lang.reflect.Field;

public class FinalTest {
    public static void main(String[] args) {
        final String str = "ABCD";
        System.out.println("str = " + str.toString());
        System.out.println("hashCode = " + str.hashCode());
        try {
            Field valueField = String.class.getDeclaredField("value");
            //value域是final private的,这里设置可访问
            valueField.setAccessible(true);
            char[] valueCharArr = (char[]) valueField.get(str);
            valueCharArr[0] = 'G';
            //此处输出第一组结果
            System.out.println("str = " + str.toString());
            System.out.println("hashCode = " + str.hashCode());
            valueField.set(str, new char[] {'1', '2'});
            //此处输出第二组结果
            System.out.println("str = " + str.toString());
            System.out.println("hashCode = " + str.hashCode());
        } catch (NoSuchFieldException | SecurityException
                | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

It seems that the value has been changed again, so what is the reason?

It turns out that if you use final to modify the data of the basic data type and String, as long as the right side is a constant expression when assigning, the compiler will be optimized. When you print, it will be

System.out.println("str = " + str);

This sentence is directly compiled into

System.out.println("str = " + "ABCD");

Therefore, in the second code, you can't modify it anyway, because the JVM has been optimized at compile time. And when you call toString () method, you get the new value modified by reflection.

in conclusion:

     When defined as data and String of basic data types, as long as the right side is a constant expression when assigning values, compiler optimization will be performed. As long as the final property that will not be optimized inline by the compiler can be effectively reflected by reflection modify

So what are the ways to avoid jvm's compile-time optimization?

1 . To change the way the assignment, automatic optimization of cancellation compiled, such as ternary expression evaluation

final String str= (null!=null?"":"42");

Method 2. Define first and then assign value, use blank final

static final int primitiveInt;
    static final Integer i;
    static String str;
 
    static {//这里改为用静态代码块赋值
        primitiveInt = 42;
        wrappedInt = 42;
        stringValue = "42";
    }

 

 

 

 

Published 42 original articles · won praise 1 · views 4690

Guess you like

Origin blog.csdn.net/thetimelyrain/article/details/105368076