您的位置:首页 > 编程语言 > Java开发

如何绕过Java的构造方法来创建实例

2014-05-26 10:01 417 查看
我所设计的绝大部分类,我都会关注它们的不可变性。要想获得不可变性需要这样做:

使用构造方法来初始化所有的属性。
这些属性没有setter方法。
然而,这样的设计使得测试更加复杂甚至无法测试。为了能进行测试,你还需要一个public的无参构造方法。

其它需要无参构造方法的情况包括:

序列化对象的反序列化。
子类中没有调用父类的构造函数。
其它
下面是它的一些解决方案。

实现一个public的无参构造方法
最简单的方式就是创建一个public的无参构造函数了,然后添加一个大的醒目的警告信息让开发人员别去使用这个方法。你可以想像得到,这种方法虽然简单,但是它无法强制约束什么,因为你得依赖开发人员的自觉性来遵循你的规则(或者更多地是他们得能在第一时间看到这段警告文档——这得赌一下了)。

这么做最大的限制就是你得能够修改类的代码。

实现一个包可见的无参构造方法
测试时一个常见的方法就是将类的private方法的可见性改成包可见的,这样的话测试类只要和它们在一个包下面就可以进行测试了。同样的方法也适用于我们这个例子中:实现一个包可见的无参构造方法。

这需要测试类和这个创建了构造函数的类在同一个包底下。和情况1类似,你也得去修改类的代码。

使用Unsafe来实现
JDK像一座埋藏的宝藏:它包含许多隐藏的闪亮的特性;sun.misc.Unsafe就是其中之一。当然了,正如它的名字和所在包所暗示的那样,它的使用是非常不推荐的。Unsafe提供了一个allocateInstance的方法来创建新的实例,而不用调用任何构造函数,也就是不需要调用任何初始化程序。

注意Unsafe只有实例方法,而它仅有的一个构造函数是私有的。。但它提供了一个私有的单例属性。要想获取这个属性的引用,你需要用到一点反射的逻辑,以及一个宽松的安全管理器(SecurityManager)。

Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);

java.sql.Date date = (java.sql.Date) unsafe.allocateInstance(java.sql.Date.class);


这个方法最大的限制在在:

依赖公共API以外的类
使用反射来访问私有属性
只在Oracle的HotSpot JVM上可用
需要设置一个足够宽松的安全管理器
ObjenesisObjenesis是一个旨在不使用构造函数创建新的实例的一个框架。它基于Unsafe提供了一个抽象层。Objenesis在不同的JVM上也同样可用,包括不同版本的OpenJDK, Oracle的JRokkit和Dalvik(也就是Android的),它使用了不同的策略来适配不同的JVM以及不同的版本的组合。
上述的代码可以替换成这面这个:

Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator instantiator = objenesis.getInstantiatorOf(java.sql.Date.class);

java.sql.Date date = (java.sql.Date) instantiator.newInstance();
System.out.println(date);


在Oracle的HotSpot上运行这段代码同样需要一个宽松的安全管理器,因为Objenesis使用的也是上面的Unsafe类。然而,不同的JVM可能会有不同的要求,这些Objenesis都替你处理了。

结论

尽管并不常见也没有专门的要求,但有时候还是需要不使用构造函数来创建实例的。万一碰上这样的情况,Objenesis框架为你提供了一个可移植的抽象层来实现这个,你只需要多增加一个额外的依赖就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐