0x01 前言
sun.misc.Unsafe
是Java为自己而生的底层API
正常情况下开发者是无法直接实例化sun.misc.Unsafe
类的,只能通过反射来获取实例
并且该类在Java的大版本中
例如:Java8, Java11 可以使用的方法也是不同的
0x02 sun.misc.Unsafe 代码片段讲解
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package sun.misc;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
public final class Unsafe {
private static final Unsafe theUnsafe;
省去其它不重要代码......
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
省去其它不重要代码......
static {
registerNatives();
Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
theUnsafe = new Unsafe();
省去其它不重要代码......
}
}
从上面的片段代码中可以看到Unsafe
无法被继承也无法通过new
的方式创建Unsafe
类实例
如果想通过getUnsafe()
方法获取Unsafe
实例还会检查类加载器
可以看到虽然getUnsafe()
方法是可以被调用,但是会有判断
内部会检查该CallerClass
是不是由系统类加载器BootstrapClassLoader
加载
也就是说默认情况下只允许由系统类加载器BootstrapClassLoader
加载
因此如果想要拿到Unsafe类实例那么最优方案既是反射的方式
0x03 反射获取Unsafe类实例
0x03.1 方法一
package UnsafeTest;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) {
// 反射获取 Unsafe 的 theUnsafe 成员变量
Field unsafeField = null;
try {
unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
// 忽略访问修饰符的检查,这样就可以调用私有方法或是成员变量
unsafeField.setAccessible(true);
// 通过 unsafeField 得到该 Field 对应的具体对象
// 传入 null 是因为该 Field 为 static
Unsafe unsafe = (Unsafe) unsafeField.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
0x03.2 方法二
package UnsafeTest;
import sun.misc.Unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test2 {
public static void main(String[] args) {
// 获取Unsafe无参构造方法
Constructor constructor = null;
try {
constructor = Unsafe.class.getDeclaredConstructor();
// 忽略访问修饰符的检查,这样就可以调用私有方法或是成员变量
constructor.setAccessible(true);
// 反射创建Unsafe类实例
// 等于 Unsafe unsafe2 = new Unsafe();
Unsafe unsafe2 = (Unsafe) constructor.newInstance();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
0x04 总结
通过 0x03
就可以获取到Unsafe
对象了,也就获得了调用Unsafe
内部方法的权限