如果我们使用反射更改最终 属性 值,则不会抛出 UnsupportedEncodingException
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);
}
}
上面的代码在使用"\"进行编码时抛出异常UnsupportedEncodingException,但是当我们使用反射修改值时却没有任何异常,请看下面的代码:
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);
}
}
为什么会有这种奇怪的行为?
编译时间常数的值由编译器内联。
这是什么意思?
假设有 private static final String STR = "Foo";
。在这里我们确定(使用标准语言规则,不包括反射,因为它是一种打破语言强制执行的所有保证的工具,例如:阻止访问 private
成员、修改 final
等)STR
应该总是 "Foo"
并且该信息在编译时也是已知的。
这允许编译器优化像 System.out.println(STR);
这样的代码,并且不需要查找 STR
的值,而是直接使用它,这将产生与我们编写 System.out.println("Foo");
相同的字节码(因为值是编译器已知的,并且“理论上”总是相同的)。
因此,即使我们使用反射并将新值重新分配给 STR
,它也不会影响表示 System.out.println("Foo");
的字节码,因为它不再依赖于 STR
.
你的情况
String s= URLEncoder.encode("anyValue", ENCODING);
将像您将其编写为
一样进行编译
String s= URLEncoder.encode("anyValue", "UTF-8");
(因为 ENCODINT
的 private static final String ENCODING = "UTF-8";
值是 "UTF-8"
并且在使用 ENCODINT
的地方被内联)。因此,即使您将新值分配给 ENCODINT
(此处为“\UTF-8”),它也不会影响表示 URLEncoder.encode("anyValue", "UTF-8")
的字节码,因为它不会 use/refer至 ENCODING
.
如果您想防止内联,请不要使 ENCODING
成为 编译 时间常数。换句话说,确保分配的值需要在运行时“计算”。例如,您可以使用 private static final String ENCODING = "UTF-8".substring(0);
.
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);
}
}
上面的代码在使用"\"进行编码时抛出异常UnsupportedEncodingException,但是当我们使用反射修改值时却没有任何异常,请看下面的代码:
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);
}
}
为什么会有这种奇怪的行为?
编译时间常数的值由编译器内联。
这是什么意思?
假设有 private static final String STR = "Foo";
。在这里我们确定(使用标准语言规则,不包括反射,因为它是一种打破语言强制执行的所有保证的工具,例如:阻止访问 private
成员、修改 final
等)STR
应该总是 "Foo"
并且该信息在编译时也是已知的。
这允许编译器优化像 System.out.println(STR);
这样的代码,并且不需要查找 STR
的值,而是直接使用它,这将产生与我们编写 System.out.println("Foo");
相同的字节码(因为值是编译器已知的,并且“理论上”总是相同的)。
因此,即使我们使用反射并将新值重新分配给 STR
,它也不会影响表示 System.out.println("Foo");
的字节码,因为它不再依赖于 STR
.
你的情况
String s= URLEncoder.encode("anyValue", ENCODING);
将像您将其编写为
一样进行编译String s= URLEncoder.encode("anyValue", "UTF-8");
(因为 ENCODINT
的 private static final String ENCODING = "UTF-8";
值是 "UTF-8"
并且在使用 ENCODINT
的地方被内联)。因此,即使您将新值分配给 ENCODINT
(此处为“\UTF-8”),它也不会影响表示 URLEncoder.encode("anyValue", "UTF-8")
的字节码,因为它不会 use/refer至 ENCODING
.
如果您想防止内联,请不要使 ENCODING
成为 编译 时间常数。换句话说,确保分配的值需要在运行时“计算”。例如,您可以使用 private static final String ENCODING = "UTF-8".substring(0);
.