JAVA单例模式深入,序列化以及反序列化获取对象实例
2017-09-15 00:00
806 查看
针对于上次分享中提出的问题这里简单的先阐述一下。先回答第二个问题。【对于问题有疑问的看官,请点击这里:第一篇分享】。
通过反射机制实现 【这里通过反射有两种实现方式:a、Class类的newInstance方法创建对象 b、java.lang.relect.Constructor类里也有一个newInstance方法可以创建对象】
通过clone方法实现 【该方法需要让当前类实现Cloneable接口 重写clone方法】
通过序列化以及反序列化实现 【该方法需要当前类实现】
这里通过懒汉式给大家演示出现的问题:
单例代码
测试代码:
测试类
测试结果:
测试结果
结论分析:通过序列化写出的内容,在通过反序列化写入,会得到一个不同的对象。
解决办法:在懒汉式的实体类中增加下面方法即可。
readResolve()方法
结论:类中具有一个私有的被实例化的方法readresolve(),这个方法可以确保类的开发人员在序列化将会返回怎样的对象。这样做我们就防止序列化以及反序列化的漏洞了【ps:jdk1.5之后enum枚举自动帮助我们处理了readresolve的情况 O(∩_∩)O~~】
反射测试
运行结果:
运行结果
总结:通过反射调用了私有构造器,重新创建了对象。
解决办法:(完整版懒汉式代码)
PS:这里有问题哦!!!
静态内部类实现单例模式
测试案例:
测试用例
测试结果:
结果
枚举单例实现
测试:
枚举测试
输出结果:输出结果为true。
问题阐述:普通的饿汉式,懒汉式以及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培训,名师亲授+项目实战,快速实现高薪就业!相关文章推荐
- c# 使用 Newtonsoft.Json 序列化json字符串以及,反序列化对象
- 深入理解Java和Android对象序列化以及反序列化
- FastJson解析实例--对象序列化和反序列化
- 用 XStream 序列化/反序列化 XML 为 Java 对象(实例)
- js利用for in循环获取 一个对象的所有属性以及值的实例
- 序列化、反序列化的版本控制以及序列化、反序列化集合对象
- Gson 序列化对象和反序列化 实例
- 用 XStream 序列化/反序列化 XML 为 Java 对象(实例)
- Java 对象序列化详解以及实例实现和源码下载
- 用 XStream 序列化/反序列化 XML 为 Java 对象(实例)
- 通过序列化和反序列化实现对象实例化
- 使用DataContractJsonSerializer类将类型实例序列化为JSON字符串和反序列化为实例对象
- 实例分析java对象的序列化和反序列化
- .NET对象的XML序列化和反序列化实例详解
- 通过序列化和反序列化实现对象实例化
- C# XmlSerializer将对象序列化以及反序列化(Sqlite数据库)
- Android中传递对象的三种方法以及Java对象的序列化和反序列化实践
- 获取PDH性能对象列表之三-------------获取性能对象的属性列表以及实例列表
- 深入理解java.lang.Class类以及class类获取实例的三种方法
- Redis+protostuff 实现对象序列化保存,反序列化获取