ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# `sun.misc.Unsafe`类的用法 > 原文: [https://howtodoinjava.com/java/basics/usage-of-class-sun-misc-unsafe/](https://howtodoinjava.com/java/basics/usage-of-class-sun-misc-unsafe/) 这篇文章是有关 [**Java**](//howtodoinjava.com/tag/java-hidden-features/ "java hidden features") 鲜为人知的特性的讨论顺序的下一个更新。 请**通过电子邮件**订阅,以在下一次讨论进行时进行更新。 并且不要忘记在评论部分表达您的观点。 Java 是一种安全的编程语言,可以防止程序员犯很多愚蠢的错误,这些错误大多数是基于内存管理的。 但是,如果您决定将其弄乱,则可以使用`Unsafe`类。 此类是`sun.*`API,其中**并不是 J2SE** 的真正组成部分,因此您可能找不到任何正式文档。 可悲的是,它也没有任何好的代码文档。 ## `sun.misc.Unsafe`的实例化 如果尝试创建`Unsafe`类的实例,则由于两个原因,将不允许您这样做。 1)不安全类具有私有构造器。 2)它也具有静态的`getUnsafe()`方法,但是如果您朴素地尝试调用`Unsafe.getUnsafe()`,则可能会得到`SecurityException`。 此类只能从受信任的代码实例化。 但是总有一些解决方法。 创建实例的类似的简单方法是使用反射: ```java Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); ``` 注意:您的 IDE,例如 eclipse 可能显示与访问限制有关的错误。 不用担心,继续运行程序。 它将运行。 现在到主要部分。 使用此对象,您可以执行“有趣的”任务。 ## `sun.misc.Unsafe`的使用 **1)创建不受限制的实例** 使用`allocateInstance()`方法,您可以创建类的实例,而无需调用其构造器代码,初始化代码,各种 JVM 安全检查以及所有其他低级内容。 即使类具有私有构造器,也可以使用此方法创建新实例。 所有单例爱好者的噩梦。 伙计们,您只是无法轻松应对这种威胁。 举个例子: ```java public class UnsafeDemo { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); //This creates an instance of player class without any initialization Player p = (Player) unsafe.allocateInstance(Player.class); System.out.println(p.getAge()); //Print 0 p.setAge(45); //Let's now set age 45 to un-initialized object System.out.println(p.getAge()); //Print 45 System.out.println(new Player().getAge()); //This the normal way to get fully initialized object; Prints 50 } } class Player{ private int age = 12; public Player(){ //Even if you create this constructor private; //You can initialize using Unsafe.allocateInstance() this.age = 50; } public int getAge(){ return this.age; } public void setAge(int age){ this.age = age; } } Output: 0 45 50 ``` **2)使用直接内存访问的浅克隆** 通常如何进行浅克隆? 在`clone(){..}`方法中调用`super.clone()`吧? 这里的问题是,您必须实现`Cloneable`接口,然后在要实现浅层克隆的所有类中覆盖`clone()`方法。 懒惰的开发人员要付出太多努力。 我不建议这样做,但是使用不安全的方法,我们可以在几行中创建浅表克隆,最好的部分是它可以与任何类一起使用,就像某些工具方法一样。 诀窍是将对象的字节复制到内存中的另一个位置,然后将该对象类型转换为克隆的对象类型。 **3)黑客的密码安全性** 这看起来很有趣吗? 是的,是的。 开发人员创建密码或将密码存储在字符串中,然后在应用程序代码中使用它们。 使用密码后,更聪明的开发人员将字符串引用设置为`NULL`,以便不再对其进行引用,并且可以轻松地对其进行垃圾回收。 但是从那时起,您对垃圾回收器启动时的引用为`null`,该字符串实例位于字符串池中。 对您的系统进行的复杂攻击将能够读取您的内存区域,从而也可以访问密码。 机会很低,但他们在这里。 因此,建议使用`char []`存储密码,以便在使用后可以遍历数组并使每个字符变脏/变空。 另一种方法是使用我们的魔术类“不安全”。 在这里,您将创建另一个长度与密码相同的临时字符串,并存储“`?`”或为临时密码中的每个字符输入“`*`”(或任何字母)。 完成密码逻辑操作后,您只需将临时密码(例如`????????`)的字节复制到原始密码上即可。 这意味着用临时密码覆盖原始密码。 示例代码如下所示。 ```java String password = new String("l00k@myHor$e"); String fake = new String(password.replaceAll(".", "?")); System.out.println(password); // l00k@myHor$e System.out.println(fake); // ???????????? getUnsafe().copyMemory(fake, 0L, null, toAddress(password), sizeOf(password)); System.out.println(password); // ???????????? System.out.println(fake); // ???????????? ``` 在运行时动态创建类 我们可以在运行时创建类,例如从已编译的`.class`文件中。 要将读取的类内容执行到字节数组,然后将其传递给`defineClass`方法。 ```java //Sample code to craeet classes byte[] classContents = getClassContent(); Class c = getUnsafe().defineClass(null, classContents, 0, classContents.length); c.getMethod("a").invoke(c.newInstance(), null); //Method to read .class file private static byte[] getClassContent() throws Exception { File f = new File("/home/mishadoff/tmp/A.class"); FileInputStream input = new FileInputStream(f); byte[] content = new byte[(int)f.length()]; input.read(content); input.close(); return content; } ``` **4)超大数组** 如您所知,`Integer.MAX_VALUE`常量是 java 数组的最大大小。 如果要构建真正的大数组(尽管在常规应用程序中没有实际需要),则可以为此使用直接内存分配。 以此类的示例为例,该类创建的顺序存储器(数组)的大小是允许的大小的两倍。 ```java class SuperArray { private final static int BYTE = 1; private long size; private long address; public SuperArray(long size) { this.size = size; address = getUnsafe().allocateMemory(size * BYTE); } public void set(long i, byte value) { getUnsafe().putByte(address + i * BYTE, value); } public int get(long idx) { return getUnsafe().getByte(address + idx * BYTE); } public long size() { return size; } } ``` 用法示例: ```java long SUPER_SIZE = (long)Integer.MAX_VALUE * 2; SuperArray array = new SuperArray(SUPER_SIZE); System.out.println("Array size:" + array.size()); // 4294967294 for (int i = 0; i < 100; i++) { array.set((long)Integer.MAX_VALUE + i, (byte)3); sum += array.get((long)Integer.MAX_VALUE + i); } System.out.println("Sum of 100 elements:" + sum); // 300 ``` 请注意,这会导致 JVM 崩溃。 ## 总结 `sun.misc.Unsafe`提供了几乎无限的能力来探索和修改 VM 的运行时数据结构。 尽管事实上这些特性几乎不能在 Java 开发本身中使用,但是对于希望在不进行 C++代码调试的情况下研究 HotSpot VM 或需要创建临时分析工具的任何人来说,`Unsafe`都是一个不错的工具。 **参考:** <http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/> **祝您学习愉快!**