
确保每个类只有一个实例而且自行实例化并向整个系统提供这个实例。单例模式避免了状态不一致的情况。特点·单例类只有一个实例·单例类自己创建那个唯一的实例·单例类为整个系统的其他对象提供这一实例。单例模式保证了全局对象的唯一性。例如配置类等。单例的四大原则·构造私有·以静态方法或者枚举方法返回实例·确保只有一个实例尤其是多线程环境·确保反序列化不会构建对象。实现实例化的方法有饿汉式立即加载public class Singleton { private Singleton() { System.out.println(构造函数Singleton); } private static Singleton single new Singleton(); public static Singleton getInstance() { System.out.println(getInstance); return single; } }懒汉式延迟加载public class Singleton { private Singleton() { System.out.println(构造函数Singleton); } private static Singleton singlenull; public static Singleton getInstance() { System.out.println(getInstance); if(singlenull){ single new Singleton(); } return single; } }同步锁解决多线程问题public class Singleton { private Singleton() { System.out.println(构造函数Singleton); } private static Singleton singlenull; public synchronized static Singleton getInstance() { System.out.println(getInstance); if(singlenull){ single new Singleton(); } return single; } }双重检查锁解决多线程下使用同步锁的性能问题为了禁止指令重排序保证对象在“引用被赋值”之前“已经完全初始化”实例变量要使用volatile修饰。在JVM中执行 single new Singleton()这行代码在字节码层面并不是原子操作而是分为三个步骤分配内存在堆中开辟一块内存空间此时内存中全是默认值比如int0引用null。初始化对象调用构造器将内存中的值填充为真正的初始值比如int5引用指向具体对象。建立关联赋值将堆内存的地址赋值给栈中的 single 引用变量。不使用volatile修饰可能会导致步骤2未执行步骤3已经执行其他线程会判断single 不为null而直接使用但是single并未初始化而导致空指针异常。public class Singleton { private Singleton() { System.out.println(构造函数Singleton); } private volatile static Singleton singlenull; public static Singleton getInstance() { System.out.println(getInstance); if(singlenull){ synchronized (Singleton.class) { if(singlenull) { single new Singleton(); } } } return single; } }内部静态类懒汉式延迟加载线程安全但是不能解决反序列化下重新构建实例的问题原理延迟加载和JVM类加载锁线程安全唯一的实例化时机Java类加载的“初始化阶段”是由JVM底层加锁进行的。当外部类Singleton 被加载时内部静态类InnerObject 不会被加载。只有第一次调用getInstance()方法时才会加载和初始化内部静态类。线程安全JVM在类初始化时会获得初始化锁因此多个线程同时调用getInstance()方法内部静态类也只会初始化一次。虽然构造器是private的但是可以通过反射强行调用构造器因此使用反射仍可以创建第二个实例。public class Singleton { private Singleton() { System.out.println(构造函数Singleton); } private static class InnerObject { private static final Singleton single new Singleton(); } public static Singleton getInstance() { return InnerObject.single; } }内部枚举类实现饿汉式防止反射和序列化攻击枚举类enum会继承java.lang.Enum 的 final 类。枚举常量会被编译成静态成员public static final修饰在静态代码块中实例化同样会获得类加载锁保证了实例的唯一性。Java反射会有检验通过反射创建枚举实例时会抛出异常IllegalArgumentException。枚举的序列化只写入常量的name反序列化会从缓存哈希表中获取已经存在的单例不会重新构建。枚举类已经继承Enum无法继承其他类。public class SingletonFactory { private enum EnumSingleton { Singleton; private Singleton singleton; private EnumSingleton() { singleton new Singleton(); } public Singleton getInstance() { return singleton; } } public static Singleton getInstance(){ return EnumSingleton.Singleton.getInstance(); } } public class Singleton { private Singleton() { System.out.println(构造函数Singleton); } }