diff --git a/docs/java/basis/unsafe.md b/docs/java/basis/unsafe.md index e99ddfd7..562a90a7 100644 --- a/docs/java/basis/unsafe.md +++ b/docs/java/basis/unsafe.md @@ -304,6 +304,49 @@ public boolean validate(long stamp) { #### 介绍 +**例子** + +```java +import sun.misc.Unsafe; +import java.lang.reflect.Field; + +public class Main { + + private int value; + + public static void main(String[] args) throws Exception{ + Unsafe unsafe = reflectGetUnsafe(); + assert unsafe != null; + long offset = unsafe.objectFieldOffset(Main.class.getDeclaredField("value")); + Main main = new Main(); + System.out.println("value before putInt: " + main.value); + unsafe.putInt(main, offset, 42); + System.out.println("value after putInt: " + main.value); + System.out.println("value after putInt: " + unsafe.getInt(main, offset)); + } + + private static Unsafe reflectGetUnsafe() { + try { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + return (Unsafe) field.get(null); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + +} +``` + +输出结果: + +``` +value before putInt: 0 +value after putInt: 42 +value after putInt: 42 +``` + **对象属性** 对象成员属性的内存偏移量获取,以及字段属性值的修改,在上面的例子中我们已经测试过了。除了前面的`putInt`、`getInt`方法外,Unsafe 提供了全部 8 种基础数据类型以及`Object`的`put`和`get`方法,并且所有的`put`方法都可以越过访问权限,直接修改内存中的数据。阅读 openJDK 源码中的注释发现,基础数据类型和`Object`的读写稍有不同,基础数据类型是直接操作的属性值(`value`),而`Object`的操作则是基于引用值(`reference value`)。下面是`Object`的读写方法: @@ -580,7 +623,7 @@ unpark mainThread success public native long staticFieldOffset(Field f); //获取静态属性的对象指针 public native Object staticFieldBase(Field f); -//判断类是否需要实例化(用于获取类的静态属性前进行检测) +//判断类是否需要初始化(用于获取类的静态属性前进行检测) public native boolean shouldBeInitialized(Class c); ``` @@ -594,6 +637,11 @@ public class User { } private void staticTest() throws Exception { User user=new User(); + // 也可以用下面的语句触发类初始化 + // 1. + // unsafe.ensureClassInitialized(User.class); + // 2. + // System.out.println(User.name); System.out.println(unsafe.shouldBeInitialized(User.class)); Field sexField = User.class.getDeclaredField("name"); long fieldOffset = unsafe.staticFieldOffset(sexField); @@ -611,7 +659,7 @@ falseHydra 在 `Unsafe` 的对象操作中,我们学习了通过`objectFieldOffset`方法获取对象属性偏移量并基于它对变量的值进行存取,但是它不适用于类中的静态属性,这时候就需要使用`staticFieldOffset`方法。在上面的代码中,只有在获取`Field`对象的过程中依赖到了`Class`,而获取静态变量的属性时不再依赖于`Class`。 -在上面的代码中首先创建一个`User`对象,这是因为如果一个类没有被实例化,那么它的静态属性也不会被初始化,最后获取的字段属性将是`null`。所以在获取静态属性前,需要调用`shouldBeInitialized`方法,判断在获取前是否需要初始化这个类。如果删除创建 User 对象的语句,运行结果会变为: +在上面的代码中首先创建一个`User`对象,这是因为如果一个类没有被初始化,那么它的静态属性也不会被初始化,最后获取的字段属性将是`null`。所以在获取静态属性前,需要调用`shouldBeInitialized`方法,判断在获取前是否需要初始化这个类。如果删除创建 User 对象的语句,运行结果会变为: ``` truenull