Skip to content

Commit d7dd741

Browse files
authored
Merge pull request Snailclimb#2040 from paigeman/paigeman-patch-2
fix(unsafe.md): 补充个例子去除割裂感以及修正部分语误
2 parents 89d387f + 8b8436d commit d7dd741

File tree

1 file changed

+50
-2
lines changed

1 file changed

+50
-2
lines changed

docs/java/basis/unsafe.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,49 @@ public boolean validate(long stamp) {
304304

305305
#### 介绍
306306

307+
**例子**
308+
309+
```java
310+
import sun.misc.Unsafe;
311+
import java.lang.reflect.Field;
312+
313+
public class Main {
314+
315+
private int value;
316+
317+
public static void main(String[] args) throws Exception{
318+
Unsafe unsafe = reflectGetUnsafe();
319+
assert unsafe != null;
320+
long offset = unsafe.objectFieldOffset(Main.class.getDeclaredField("value"));
321+
Main main = new Main();
322+
System.out.println("value before putInt: " + main.value);
323+
unsafe.putInt(main, offset, 42);
324+
System.out.println("value after putInt: " + main.value);
325+
System.out.println("value after putInt: " + unsafe.getInt(main, offset));
326+
}
327+
328+
private static Unsafe reflectGetUnsafe() {
329+
try {
330+
Field field = Unsafe.class.getDeclaredField("theUnsafe");
331+
field.setAccessible(true);
332+
return (Unsafe) field.get(null);
333+
} catch (Exception e) {
334+
e.printStackTrace();
335+
return null;
336+
}
337+
}
338+
339+
}
340+
```
341+
342+
输出结果:
343+
344+
```
345+
value before putInt: 0
346+
value after putInt: 42
347+
value after putInt: 42
348+
```
349+
307350
**对象属性**
308351

309352
对象成员属性的内存偏移量获取,以及字段属性值的修改,在上面的例子中我们已经测试过了。除了前面的`putInt``getInt`方法外,Unsafe 提供了全部 8 种基础数据类型以及`Object``put``get`方法,并且所有的`put`方法都可以越过访问权限,直接修改内存中的数据。阅读 openJDK 源码中的注释发现,基础数据类型和`Object`的读写稍有不同,基础数据类型是直接操作的属性值(`value`),而`Object`的操作则是基于引用值(`reference value`)。下面是`Object`的读写方法:
@@ -580,7 +623,7 @@ unpark mainThread success
580623
public native long staticFieldOffset(Field f);
581624
//获取静态属性的对象指针
582625
public native Object staticFieldBase(Field f);
583-
//判断类是否需要实例化(用于获取类的静态属性前进行检测)
626+
//判断类是否需要初始化(用于获取类的静态属性前进行检测)
584627
public native boolean shouldBeInitialized(Class<?> c);
585628
```
586629

@@ -594,6 +637,11 @@ public class User {
594637
}
595638
private void staticTest() throws Exception {
596639
User user=new User();
640+
// 也可以用下面的语句触发类初始化
641+
// 1.
642+
// unsafe.ensureClassInitialized(User.class);
643+
// 2.
644+
// System.out.println(User.name);
597645
System.out.println(unsafe.shouldBeInitialized(User.class));
598646
Field sexField = User.class.getDeclaredField("name");
599647
long fieldOffset = unsafe.staticFieldOffset(sexField);
@@ -611,7 +659,7 @@ falseHydra
611659

612660
`Unsafe` 的对象操作中,我们学习了通过`objectFieldOffset`方法获取对象属性偏移量并基于它对变量的值进行存取,但是它不适用于类中的静态属性,这时候就需要使用`staticFieldOffset`方法。在上面的代码中,只有在获取`Field`对象的过程中依赖到了`Class`,而获取静态变量的属性时不再依赖于`Class`
613661

614-
在上面的代码中首先创建一个`User`对象,这是因为如果一个类没有被实例化,那么它的静态属性也不会被初始化,最后获取的字段属性将是`null`。所以在获取静态属性前,需要调用`shouldBeInitialized`方法,判断在获取前是否需要初始化这个类。如果删除创建 User 对象的语句,运行结果会变为:
662+
在上面的代码中首先创建一个`User`对象,这是因为如果一个类没有被初始化,那么它的静态属性也不会被初始化,最后获取的字段属性将是`null`。所以在获取静态属性前,需要调用`shouldBeInitialized`方法,判断在获取前是否需要初始化这个类。如果删除创建 User 对象的语句,运行结果会变为:
615663

616664
```
617665
truenull

0 commit comments

Comments
 (0)