单例模式

来源:互联网 发布:手机淘宝旺旺名怎么看 编辑:程序博客网 时间:2024/06/15 08:01

本文讲Java中单例模式的3种实现方法。

方法一

public class Elvis {public static final Elvis INSTANCE=new Elvis();        private Elvis(){}}

强制把构造函数私有化,只能通过INSTANCE来得到实例,并且这个实例只有1个。同时,INSTANCE中final类型,不可被更改。

但是客户端程序可以借助AccessibleObject.setAccessible的方法,通过反射来调用私有构造函数。代码如下:
public class Elvis {private static int count=0;public static final Elvis INSTANCE=new Elvis();private Elvis(){count++;}public static void main(String[] args){System.out.println(Elvis.INSTANCE.count);Class<?> cl=null;try {//通过反射得到类cl=Class.forName("singleton.Elvis");//获取类的构造函数Constructor constructors[]=cl.getDeclaredConstructors();//设置构造函数可访问constructors[0].setAccessible(true);//使用构造函数创建实例constructors[0].newInstance(null);//获取第一个成员(即count)的值System.out.println(cl.getDeclaredFields()[0].get(null));constructors[0].newInstance(null);System.out.println(cl.getDeclaredFields()[0].get(null));} catch (ClassNotFoundException |IllegalArgumentException |IllegalAccessException | SecurityException |InstantiationException | InvocationTargetException e) {e.printStackTrace();}}}
输出:
1
2
3
java在反射创建一个类的实例时,默认会检测是否符合相关安全,该检测开关可以关闭。Constructor、Field、Method都是继承于AccessibleObject,对应实例调用setAccessible(true)就关闭该开关。
如果要抵御这种反射攻击,可以修改构造函数,让它在创建第二个实例时抛出异常。
private Elvis(){if(count==1){System.err.println("transgress singleton");System.exit(1);}count++;}

方法二

使用工厂方法。工厂方法本身在诸多好处,这里就不细讲了。
public class Elvis2 {private static final Elvis2 INSTANCE=new Elvis2();private Elvis2(){}public static Elvis2 getInstance(){return INSTANCE;}public static void main(String[] args){Elvis2 inst1=Elvis2.getInstance();Elvis2 inst2=Elvis2.getInstance();if(inst1==inst2)System.out.println("inst1==inst2");elseSystem.out.println("inst1!=inst2");}}

输出:inst1==inst2
当然这也会遇到反射攻击。

为了使Singeleton类变成可序列化的(Serializable),仅仅在声明中加上"implements Serializable"是不够的。为了维护并保证Singeleton,必须声明所有实例域都是瞬时的(transient),并提供一个readResolve方法,否则每次反序列化的实例时,都会创建一个新的实例。所以就有了第三种方法。

方法三

编写一个包含单个元素的枚举类型。
public enum Elvis3 {INSTANCE;public int count=0;public void incease(){count++;}public static void main(String[] args){Elvis3 inst1=Elvis3.INSTANCE;inst1.incease();inst1.incease();System.out.println(inst1.count);Elvis3 inst2=Elvis3.INSTANCE;System.out.println(inst2.count);}}
单元素枚举类型已经成为实现Singeleton的最佳方式,它更加简洁,无偿提供了序列化机制,绝对防止多次实例化(即使使用复杂的序列化或反射攻击)。