java Unsafe,修正以前的一个错误认识

java Unsafe,修正以前的一个错误认识

最近看到这样一个问题:“在java中,可以不调用构造方法得到一个对象吗?”

当时我是这样思考的:“现在我想得到一个对象,我可以自己创建(new、反射newInstance),也可以clone一个现有的对象。new和反射都会调用类的构造方法,所以答案就剩下clone了。”

这个回答当然是对的,但是不完整。因为我漏掉了创建对象的第四种方式——Unsafe。

一、分析

(1)获取Unsafe对象

只能通过反射的方式获取Unsafe对象:

(2)使用Unsafe创建对象

现在有一个Player类,这个类的构造方法是私有的:

Player.java

这样一来,无论是new还是通过反射newInstance,都无法“轻易”创建这个对象了(之所以使用“轻易”进行修饰,是因为反射可以调用setAccessible方法让private构造方法变为可达,就可以创建了):

除了使用setAccessible方法这种小手段,还可以使用Unsafe。

TestUnsafe.java

运行结果为:

player对象被创建成功了,因为没有调用构造方法,所以id和name都是null。

(3)修改字段

现在改写一下Player类,删掉所有set方法:

Player.java

既然id和name都是private字段,再加上没有set方法,那是不是意味着这两个字段是不可达的了?

当然不是,Unsafe可以直接修改内存,无视java那一套规则。

TestUnsafe.java

运行结果为:

(4)java中存在绝对安全的单例模式吗?

记得以前写过一篇文章:

java 如何防止单例模式被攻击?

既然Unsafe能随心所欲地创建对象、修改对象,你还觉得你写过的单例对象是安全的吗?

事实上,这些防范手段都是防君子不防小人的…在Unsafe这种能直接操作内存的外挂前,不存在绝对安全的对象。

三、总结

Unsafe是非常常见(比如Java中原子类的CAS操作,用的就是Unsafe),同时也非常难用好的东西(更像是C,自己操作,自己负责)。更多用法可以参考:

https://leokongwq.github.io/2016/12/31/java-magic-unsafe.html#%E5%B9%B6%E5%8F%91

希望以后能用上Unsafe,提高生产力。

发布者

xie4ever

发表评论

电子邮件地址不会被公开。