Singleton指仅仅被实例化一次的类。Singleton通常被用来代表那些本质上唯一的系统组件。
实现单例模式常见有三种方式:
静态成员
package Singleton强化; public class Elvis { public static final Elvis INSTANCE=new Elvis(); private Elvis() { } public void leaveTheBuilding(){ System.out.println("Hello!..."); } public static void main(String[] args) { Elvis elvis=new Elvis(); elvis.leaveTheBuilding(); } }
静态工厂方法
package Singleton强化; public class Elvis1 { private static final Elvis1 INSTANCE =new Elvis1(); private Elvis1(){}; public static Elvis1 getInstance(){return INSTANCE;}; public void leaveTheBuilding(){ System.out.println("Hello!..."); } public static void main(String[] args) { Elvis1 elvis1=Elvis1.getInstance(); elvis1.leaveTheBuilding(); } }
但是这两种方式不能完全保证全局唯一对象。享有特权的客户端可以借助反射机制设置AccessibleObject.setAccessible(ture),改变构造器访问属性,调用私有构造器。让它在被要求创建第二个实例的时候抛出异常。
package Singleton强化; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 第二次创建实例的时候抛出异常。 */ public class Elvis1 { private static int count=0; //记录创建次数 private static final Elvis1 INSTANCE =new Elvis1(); private Elvis1(){ if (count>0){ throw new IllegalArgumentException("Cannot create Elvis1 twice"); } count++; } public static Elvis1 getInstance(){return INSTANCE;}; public void leaveTheBuilding(){ System.out.println("Hello!..."); } public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException { Elvis1 elvis1=Elvis1.getInstance(); // elvis1.leaveTheBuilding(); //手动反射去创建实例化对象 Constructor<?> constructor = Elvis1.class.getDeclaredConstructors()[0]; constructor.setAccessible(Boolean.TRUE); Elvis1 elvis11 = (Elvis1) constructor.newInstance(); elvis11.leaveTheBuilding(); } }抛出不能创建两次的异常:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at Singleton强化.Elvis1.main(Elvis1.java:30)
Caused by: java.lang.IllegalArgumentException: Cannot create Elvis1 twice
at Singleton强化.Elvis1.<init>(Elvis1.java:14)
... 5 more
如果上面两种实现的Singleton是可序列化的,加上implements Serializable只保证它是可序列化的,为了保证反序列化的时候,实例还是Singleton的,必须声明实例域都是瞬时(transient)的,并提供readResolve方法。否则,每次反序列化一个序列化实例,都会创建一个新的实例。下面的例子每次反序列化都会生成新的实例对象:
package Singleton强化; import java.io.*; public class Elvis implements Serializable{ public static final Elvis INSTANCE = new Elvis(); private Elvis() { } public void leaveTheBuilding() { System.out.println("Hell!..."); } public static void main(String[] args) throws IOException, ClassNotFoundException { Elvis elvis = new Elvis(); //elvis.leaveTheBuilding(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/1.6/elvis.txt ")); oos.writeObject(elvis); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/1.6/elvis.txt ")); Elvis o = (Elvis) ois.readObject(); ois.close(); System.out.println(elvis == o); //false } }
加入readResolve方法之后:
package Singleton强化; import java.io.*; public class Elvis implements Serializable { public static final Elvis INSTANCE = new Elvis(); private Elvis() { } public void leaveTheBuilding() { System.out.println("Hell!..."); } private Object readResolve() { // Return the one true Elvis and let the garbage collector} // take care of the Elvis impersonator.} return INSTANCE; } public static void main(String[] args) throws IOException, ClassNotFoundException { //E:/1.6/elvis.txt Elvis elvis = Elvis.INSTANCE; elvis.leaveTheBuilding(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/1.6/elvis.txt ")); oos.writeObject(elvis); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/1.6/elvis.txt ")); Elvis o = (Elvis) ois.readObject(); ois.close(); System.out.println(elvis == o); //ture } }
单元素枚举类型
单元素枚举:只需要编写一个包含单元素的枚举类型
package Singleton强化; public enum Elvis2 { INSTANCE; public void leaveTheBuilding(){ System.out.println("Hello!..."); } public static void main(String[] args) { Elvis2 elvis2=Elvis2.INSTANCE; elvis2.leaveTheBuilding(); } }
通过枚举实现Singleton更加简洁,同时枚举类型无偿提供了序列化机制,可以防止序列化的时候多次实例化同一对象。枚举类型也可以防止反射攻击,当你试图反射实例化枚举实例的时候会抛出IllegalArgumentException异常。
总结
单元素的枚举类型已经成为实现Singleton的最佳方法。