# `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/>

 **祝您学习愉快！**