简体   繁体   中英

UnsupportedEncodingException is not getting thrown, if we change final property value using reflection

package com.java.random.practice;
    
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
    
public class App 
{
    private static final String ENCODING = "\\UTF-8";
    public static void main( String[] args ) throws UnsupportedEncodingException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
        {      
            URLEncoder.encode("anyValue", ENCODING);
        }
}

Above code throws exception UnsupportedEncodingException when use "\" with encoding but when we use Reflection to modify the value then it does not show any exception, please see the below code:

package com.java.random.practice;
    
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
    
public class App 
{
    private static final String ENCODING = "UTF-8";
    public static void main( String[] args ) throws UnsupportedEncodingException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
        {      
            App app=new App(); 
            Field field = app.getClass().getDeclaredField("ENCODING");
            field.setAccessible(true); 
            Field modifiersField =
            Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true); modifiersField.setInt(field,
            field.getModifiers() & ~Modifier.FINAL); field.set(null, "\\UTF-8");
             
            String s= URLEncoder.encode("anyValue", ENCODING);
            System.out.println(s);
        }
    }

Why this kind of strange behavior?

Values of compilation time constants are inlined by compiler.

What does it mean?
Lets say there is private static final String STR = "Foo"; . Here we are sure (using standard language rules, without including reflection since it is a tool that breaks all guarantees language enforces, like: preventing accessing private members, modifying final , etc.) that value of STR should be always "Foo" and that information is also known at compilation time.

This allows compiler to optimize code like System.out.println(STR); and remove the need to lookup value of STR but instead use it directly which will produce same bytecode as if we would write System.out.println("Foo"); (since value is known to compiler and "theoretically" always the same).

So even if we use reflection and reassign new value to STR it will not affect bytecode which represents System.out.println("Foo"); since it no longer depends on STR .

In your case

String s= URLEncoder.encode("anyValue", ENCODING);

will be compiled as if you would write it as

String s= URLEncoder.encode("anyValue", "UTF-8");

(since private static final String ENCODING = "UTF-8"; value of ENCODINT is "UTF-8" and that is inlined in places where ENCODINT is being used). So even if you assign new value to ENCODINT (here "\UTF-8") it can't affect bytecode which represents URLEncoder.encode("anyValue", "UTF-8") since it doesn't use/refer to ENCODING .

If you want to prevent inlining don't make ENCODING a compilation time constant. In other words ensure that assigned value will need to be "calculated" at runtime. For instance you can use private static final String ENCODING = "UTF-8".substring(0); .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM