用私有构造器或者枚举类型强化SingleTon(单例)属性
作者:互联网
单例(singleton)就是一个只实例化一次的类。使类成为单例可能会使它的测试变得困难,因为除非它实现了作为其类型的接口,否则不可能用模拟实现来代替这个单例。下面是几种实现单例的方法:
1、共有静态成员是final类型
// Singleton with public final field public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } }
私有构造器之后执行一次,实例化Elvis.INSTANCE属性,由于缺少公有(public)的或者受保护(protected)的构造器,所以Elvis一旦被实例化,就只会存在一个Elvis的实例(注:反射是可以实现多次调用私有的构造器,若需要抵御这种攻击,则可以修改私有构造器,让它在被创建第二个实例时,抛出异常,或直接返回第一个实例对象)。
2、公有的成员是一个静态方法。
// Singleton with static factory public class Elvis { private static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public static Elvis getInstance() { return INSTANCE; } }
这个方式可以通过公有静态方法将类改为非单例,但用户代码不需要改变。
为了使利用这其中一种方法实现的SingleTon类变成是可序列化的(Serializable),仅仅在申明上加上“implements Serializable”是不够的。为了维护并保证SingleTon,必须申明所有实例都是瞬时的(transient),并提供一个readResolve方法,否者,每次反序列化一个序列化的实例时,都会创建一个新的实例,比如说,在我们的实例中,会导致“假冒的Elvis”。为了防止这种情况,要在Elvis类中加入下面这个readResolve方法:
参考地址:https://blog.csdn.net/zhushuai1221/article/details/51780895
//1、防止反序列化获取多个对象的漏洞。 //2、无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。 //3、实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。 private Object readResolve(){ return INSTANCE; }
3、使用枚举实现单例(最佳方式)
/** * 使用枚举的单例模式 * */ public class Elvis{ private Elvis(){}
public static Elvis getInstance(){ return Singleton.INSTANCE.getInstance(); } private enum Singleton{ INSTANCE; private Elvis singleton; //JVM会保证此方法绝对只调用一次 Singleton(){ singleton = new Elvis(); } public Elvis getInstance(){ return singleton; } } }
这种方法是在需要的类中编写一个包含单个元素的枚举类型。无偿的提供了序列化机制,绝对防止多次序列化,即使是在面对复杂的序列化或者反射攻击的时候。
标签:SingleTon,Elvis,INSTANCE,枚举,实例,单例,序列化,public 来源: https://www.cnblogs.com/cq-yangzhou/p/10072367.html