单例模式提供了一种创建对象的最佳方式,一个类负责创建自己的对象,同时确保只用单个对象被创建,提供了一种访问这个唯一对象的方式,可以直接访问,而不需要实例化该类的对象
·饿汉式:对象会随着类的加载被创建,如果没有使用到的话,就会造成资源浪费
- 静态变量创建对象:不对外提供构造方法,使用私有静态创建这个对象,对外提供一个静态的方法用于返回创建的这个对象
- 静态代码块创建对象:不对外提供构造方法,定义这个对象,不对这个对象进行赋值,对象的赋值在static{}里面完成,对外提供一个静态的方法用于返回创建的这个对象,静态代码块在类的加载时就会被调用,而且只会调用一次
- 枚举类方式:在枚举中定义枚举类,枚举的字段,在枚举的构造方法中赋值即可,之后从枚举中取出赋值即可,枚举只会装载一次,而且线程安全,所以不考虑内存占用推荐用这个
·懒汉式:第一次调用的时候才会被加载,之后就使用这个被加载出来的对象
- 双重检查锁创建:不对外提供构造方法,定义这个对象,不对这个对象进行赋值,对外提供一个静态方法用于创建并返回这个对象,方法里需要判断对象是否为空,为空给对象赋值,非空直接返回,防止多线程情况下不安全,需要给赋值方法加锁,并且里面再次进行一次非空判断,不然多个线程有可能同时去给这个对象赋值,为了保证有序性,可以给对象加volatile关键字
- 静态内部类创建:jvm加载外部类的时候是不会去加内部类的,而是调用的时候去加载,所以可以在类里面定义一个静态内部类,在里面完成对象的创建,并用final修饰,外部返回这个静态内部类的对象即可,而且类的加载过程中jvm是会自动给它加锁的,所以也保证了线程的安全
破坏单例模式的方法(枚举类无法破坏):
·序列化
- 通过序列化对象,然后反序列化对象来创建,相当于是直接指向了一个新的内存地址,所以反序列化多个对象的地址是不一样的
- 在类中定义一个readResolve方法,返回单例的对象,反序列化时底层会有一个判断对象里是否有这个方法,有就使用这个方法的返回值充当反序列化出来的对象
·反射
- 通过反射获得对象的私有构造方法创建对象,这样相当于无视了单例模式
- 定义一个布尔值为false,在私有构造方法中判断,如果布尔值为false,说明第一次创建,允许创建,把布尔值设为true,如果为true,说明创建过对象,再次创建就拋一个异常来阻止创建