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

JAVA单例模式深入,序列化以及反序列化获取对象实例

2017-09-15 00:00 806 查看
针对于上次分享中提出的问题这里简单的先阐述一下。先回答第二个问题。【对于问题有疑问的看官,请点击这里:第一篇分享】。

问题阐述:普通的饿汉式,懒汉式以及DCL是否真的能够做到单例?

要解决这个问题,首先思考,传统的创建对象的方式是通过new关键词,调用对应类中的构造器,完成对象的实例化过程。而单例只是将构造器私有,简单点只是做了一个修饰符的转换,有没有可能不通过构造器而创建一个实例,如果可以,那么上述的三种方式就不能真正做到单例。

创建对象的方式:

通过new关键词 【也只最常用的一种实现方式】

通过反射机制实现 【这里通过反射有两种实现方式:a、Class类的newInstance方法创建对象 b、java.lang.relect.Constructor类里也有一个newInstance方法可以创建对象】

通过clone方法实现 【该方法需要让当前类实现Cloneable接口 重写clone方法】

通过序列化以及反序列化实现 【该方法需要当前类实现】

总结

调用构造器的实现方式,分别是new以及反射,而这里还可以采用clone以及序列化的方式创建对象,那么这就有了漏洞可钻。

这里通过懒汉式给大家演示出现的问题:

序列化以及反序列化获取对象实例

代码实例演示

单例代码:



单例代码

测试代码:



测试类

测试结果:



测试结果

结论分析:通过序列化写出的内容,在通过反序列化写入,会得到一个不同的对象。

解决办法:在懒汉式的实体类中增加下面方法即可。



readResolve()方法

结论:类中具有一个私有的被实例化的方法readresolve(),这个方法可以确保类的开发人员在序列化将会返回怎样的对象。这样做我们就防止序列化以及反序列化的漏洞了【ps:jdk1.5之后enum枚举自动帮助我们处理了readresolve的情况 O(∩_∩)O~~】

PS: 关于clone创建对象,我们留在分析克隆模式再给大家详细分享

通过反射获取单例对象:

案例测试(用例代码不变,改变测试代码)

测试代码:



反射测试

运行结果:



运行结果

总结:通过反射调用了私有构造器,重新创建了对象。

解决办法:(完整版懒汉式代码)



PS:这里有问题哦!!!

最后我们在看看其他两种单例实现方式:

静态内部类实现单例

案例演示:



静态内部类实现单例模式

测试案例:



测试用例

测试结果:



结果

总结:静态内部类式和饿汉式一样,同样利用了ClassLoader的机制保证了线程安全;不同的是,饿汉式在Singleton类被加载时就创建了一个实例对象,而静态内部类即使Singleton类被加载也不会创建单例对象,除非调用里面的getInstance()方法。因为当Singleton类被加载时,其静态内SingletonHolder没有被主动使用。只有当调用getInstance方法时,才会装载SingletonHolder类,从而实例化单例对象。这样,通过静态内部类的方法就实现了lazy loading,很好地将懒汉式和饿汉式结合起来,既实现延迟加载,保证系统性能,也能保证线程安全 。

通过枚举类实现单例

案例演示:



枚举单例实现

测试:



枚举测试

输出结果:输出结果为true。

结论:jvm提供底层保证 不可能出现序列化、反射产生对象的漏洞 但是不能做到延迟加载在外部,可以通过EnumSingleton.INSTANCE.方法()来调用方法,做到延迟加载。默认的枚举实例的创建是线程安全的,但是实例内的各种方法则需要程序员来保证线程安全。总的来说,使用枚举单例模式,有三个好处:

1.实例的创建线程安全,确保单例。2.防止被反射创建多个实例。3.没有序列化的问题。

上海尚学堂java培训,名师亲授+项目实战,快速实现高薪就业!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java EE JVMTI 单例模式