单例模式
饿汉式(静态常量):
/** * 饿汉式(静态常量) * * 1.构造器私有 * 2.定义一个静态常量保存一个唯一的实例对象(单例) * 3.提供一个静态方法返回单例对象 */ public class Singleton01 { public static final Singleton01 INSTANCE = new Singleton01(); private Singleton01() {} public static Singleton01 getInstance() { return INSTANCE; } } class Test01 { public static void main(String[] args) { Singleton01 s1 = Singleton01.getInstance(); Singleton01 s2 = Singleton01.getInstance(); System.out.println(s1 == s2); // true } }
饿汉式(静态代码块):
/** * 饿汉式(静态代码块) * * 1.构造器私有 * 2.定义一个静态常量保存一个唯一的实例对象(单例),通过静态代码块初始化单例对象 * 3.提供一个静态方法返回单例对象 */ public class Singleton02 { public static final Singleton02 INSTANCE; static { INSTANCE = new Singleton02(); } private Singleton02() {} public static Singleton02 getInstance() { return INSTANCE; } } class Test02 { public static void main(String[] args) { Singleton02 s1 = Singleton02.getInstance(); Singleton02 s2 = Singleton02.getInstance(); System.out.println(s1 == s2); // true } }
懒汉式(线程不安全,不推荐这样写):
/** * 懒汉式(线程不安全,不推荐这样写) * * 1.构造器私有 * 2.定义一个静态变量存储一个单例对象(定义时不初始化该对象) * 3.提供一个静态方法返回单例对象,返回前先询问是否有,有则直接返回,没有则创建新的返回 */ public class Singleton03 { public static Singleton03 instance; private Singleton03() {} public static Singleton03 getInstance() { if (instance == null) { instance = new Singleton03(); } return instance; } } class Test03 { public static void main(String[] args) { Singleton03 s1 = Singleton03.getInstance(); Singleton03 s2 = Singleton03.getInstance(); System.out.println(s1 == s2); // true / false } }
懒汉式(线程安全,性能差,不推荐这样写):
/** * 懒汉式(线程安全,性能差,不推荐这样写) * * 1.构造器私有 * 2.定义一个静态变量存储一个单例对象(定义时不初始化该对象) * 3.提供一个静态同步方法返回单例对象,返回前先询问是否有,有则直接返回,没有则创建新的返回 */ public class Singleton04 { public static Singleton04 instance; private Singleton04() {} public static synchronized Singleton04 getInstance() { if (instance == null) { instance = new Singleton04(); } return instance; } } class Test04 { public static void main(String[] args) { Singleton04 s1 = Singleton04.getInstance(); Singleton04 s2 = Singleton04.getInstance(); System.out.println(s1 == s2); // true } }
懒汉式(线程不安全,不推荐这样写):
/** * 懒汉式(线程不安全,不推荐这样写) * * 1.构造器私有 * 2.定义一个静态变量存储一个单例对象(定义时不初始化该对象) * 3.提供一个静态方法返回单例对象,返回前先询问是否有,有则直接返回,没有则创建新的返回 */ public class Singleton05 { public static Singleton05 instance; private Singleton05() {} public static Singleton05 getInstance() { if (instance == null) { // 性能得到了优化,但是依然不能保证第一次获取对象的线程安全 synchronized (Singleton05.class) { instance = new Singleton05(); } } return instance; } } class Test05 { public static void main(String[] args) { Singleton05 s1 = Singleton05.getInstance(); Singleton05 s2 = Singleton05.getInstance(); System.out.println(s1 == s2); // true / false } }
懒汉式(线程安全):其实私有化构造器并不保险,存在反射攻击;还可能出现反序列化攻击的风险
/** * 懒汉式(线程安全) * * 1.构造器私有 * 2.定义一个静态变量(用volatile修饰,保证可见性和禁止指令重排序)存储一个单例对象(定义时不初始化该对象) * 3.提供一个静态方法返回单例对象,返回前先询问是否有,有则直接返回,没有则创建新的返回 */ public class Singleton06 { public static volatile Singleton06 instance; private Singleton06() {} public static Singleton06 getInstance() { if (instance == null) { synchronized (Singleton06.class) { if (instance == null) { instance = new Singleton06(); } } } return instance; } } class Test06 { public static void main(String[] args) { Singleton06 s1 = Singleton06.getInstance(); Singleton06 s2 = Singleton06.getInstance(); System.out.println(s1 == s2); // true } }
懒汉式(基于类的初始化实现延迟加载和线程安全的单例设计):存在反射攻击和反序列化攻击的风险
/** * 懒汉式(基于类的初始化实现延迟加载和线程安全的单例设计) * * 1.构造器私有 * 2.提供一个静态内部类,里面提供一个常量存储一个单例对象 * 3.提供一个静态方法返回静态内部类中的单例对象 */ public class Singleton07 { /*private static int i = 0; private int j = 0;*/ private Singleton07() {} // 静态内部类只有等到需要用到才会加载,在加载过程中JVM会获取一个锁,不会出现线程安全问题,且只加载一次 /*静态内部类有一个局限性,只能访问外部类静态的变量,非静态的无法访问*/ private static class Inner { private static final Singleton07 INSTANCE = new Singleton07(); /*private void test() { i += 1; // j += 1; // 无法访问 }*/ } public static Singleton07 getInstance() { return Inner.INSTANCE; } } class Test07 { public static void main(String[] args) { Singleton07 s1 = Singleton07.getInstance(); Singleton07 s2 = Singleton07.getInstance(); System.out.println(s1 == s2); // true } }
枚举实现单例:枚举本身已经实现了线程安全
/** * 枚举实现单例 * * 枚举实际上是一种多例的模式,如果我们直接定义一个实例就相当于单例了 */ public enum Singleton08 { INSTANCE; public Singleton08 getInstance() { return INSTANCE; } } class test08 { public static void main(String[] args) { Singleton08 s1 = Singleton08.INSTANCE.getInstance(); Singleton08 s2 = Singleton08.INSTANCE.getInstance(); System.out.println(s1 == s2); // true } }