Java使用Unsafe修改带有final的值(字段)

请先阅读Private APIs not usable in Java 9?

环境要求,[JDK1.6, JDK1.8]或JDK9及以上包含模块jdk.unsupported。

首先,创建一个Java工程,并创建Target类,Utils类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*用途:被修改的对象
* 注意:应使用以下三种写法之一防止被内联优化。
* {public final String field=new String("a");}
* {public final String field="a".intern();}
* {public final String field;public Target(){field="a";}}
* 使用以下写法会产生内联优化,导致修改失败。
* {public final String field="a";}
* Target.java
*/
public class Target{
public final String field;
public static final String staticField;
static{
staticField="B";
}
public Target(){
field="a";
}
public void print(){
System.out.println(field);
}
public static void printStatic(){
System.out.println(staticField);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*用途:获取Unsafe对象
* Utils.java
*/
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Utils{
private static Unsafe unsafe;//缓存Unsafe对象,因为反射的速度比较慢。
public static Unsafe getUnsafe() {
if(unsafe!=null)
return unsafe;
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");//private static final
f.setAccessible(true);//相当于移除private
unsafe = (Unsafe) f.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
if(unsafe==null)
throw new NullPointerException();
return unsafe;
}
}

创建修改动态对象的类ModifyDynamic

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Field;
public class ModifyDynamic{
public static void main(String[] args) throws Exception{
Target target = new Target();
target.print();
Field f=Target.class.getDeclaredField("field");//获取字段
long offset=Utils.getUnsafe().objectFieldOffset(f);//获取字段在对象中的偏移值。
Utils.getUnsafe().putObjectVolatile(target,offset,"Z");
target.print();
}
}

动态对象修改结果

1
2
3
4
# java ModifyDynamic
a
Z
#

创建修改静态对象的类ModifyStatic

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Field;
public class ModifyStatic{
public static void main(String[] args) throws Exception{
target.printStatic();
Field f=Target.class.getDeclaredField("staticField");//获取字段
long offset=Utils.getUnsafe().staticFieldOffset(f);//此处使用staticFieldOffset来获得静态字段的偏移值
Object o=Utils.getUnsafe().staticFieldBase(f);//获取字段所在的对象,一般情况下获取的对象就是Target.class,但不排除实现不同导致含义不同。具体含义请查询文档。
Utils.getUnsafe().putObjectVolatile(o,offset,"y");
target.printStatic();
}
}

静态对象修改结果

1
2
3
4
# java ModifyStatic
B
y
#