一、懒汉式
/*** 懒汉式* 优点:需要的时候才把对象创建出来* 缺点:由于加了 synchronized 锁,所以并发时效率较低*/public class Singleton1 {private static Singleton1 singleton;private Singleton1() {}public static synchronized Singleton1 getInstance() {if (singleton == null) {singleton = new Singleton1();}return singleton;}}
二、饿汉式
/*** 饿汉式* 优点:没有加锁,性能高* 缺点:在类装载时就实例化,浪费内存*/public class Singleton2 {private static Singleton2 singleton = new Singleton2();private Singleton2() {}public static Singleton2 getInstance() {return singleton;}}
三、双重校验锁
/*** 双重校验锁* 优点:需要的时候才创建对象,由于获取对象方法没有加锁,所以性能较高* 缺点:实现较复杂*/public class Singleton3 {private static Singleton3 singleton;private Singleton3() {}public static Singleton3 getInstance() {if (singleton == null) {synchronized (Singleton3.class) {if (singleton == null) {singleton = new Singleton3();}}}return singleton;}}
四、枚举
public enum Singleton4 {INSTANCE;}
优势 1:一目了然的代码
优势 2:天然的线程安全与单一实例
它不需要做任何额外的操作,就可以保证对象单一性与线程安全性。
优势 3:枚举保护单例模式不被破坏
使用枚举可以防止调用者使用反射、序列化与反序列化机制强制生成多个单例对象,破坏单例模式。
防反射
枚举类默认继承了 Enum 类,在利用反射调用 newInstance() 时,会判断该类是否是一个枚举类,如果是,则抛出异常。
防止反序列化创建多个枚举对象
在读入 Singleton 对象时,每个枚举类型和枚举名字都是唯一的,所以在序列化时,仅仅只是对枚举的类型和变量名输出到文件中,在读入文件反序列化成对象时,使用 Enum 类的 valueOf(String name) 方法根据变量的名字查找对应的枚举对象。
所以,在序列化和反序列化的过程中,只是写出和读入了枚举类型和名字,没有任何关于对象的操作。
五、静态内部类
/*** 静态内部类* 优点:实现简单,需要使用时才加载,节约内存*/public class Singleton5 {private static class SingletonHolder {private static final Singleton5 INSTANCE = new Singleton5();}private Singleton5() {}public static Singleton5 getInstance() {return SingletonHolder.INSTANCE;}}
破坏单例的方式
利用反射破坏单例模式
利用反射,强制访问类的私有构造器,去创建另一个对象
public static void main(String[] args) {// 获取类的显式构造器Constructor<Singleton> construct = Singleton.class.getDeclaredConstructor();// 可访问私有构造器construct.setAccessible(true);// 利用反射构造新对象Singleton obj1 = construct.newInstance();// 通过正常方式获取单例对象Singleton obj2 = Singleton.getInstance();System.out.println(obj1 == obj2); // false}
利用序列化与反序列化破坏单例模式
使用序列化和反序列化破坏单例模式
public static void main(String[] args) {// 创建输出流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton.file"));// 将单例对象写到文件中oos.writeObject(Singleton.getInstance());// 从文件中读取单例对象File file = new File("Singleton.file");ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));Singleton newInstance = (Singleton) ois.readObject();// 判断是否是同一个对象System.out.println(newInstance == Singleton.getInstance()); // false}
