单例模式就是只需要创建一次,在整个应用生命周期都可以一直使用。
我们常分为饿汉式和懒汉式两种。
饿汉式
饿汉式是在初始化的时候就将单例对象创建出来。通常,通过属性new创建自身。该方式不存在线程安全的问题(JVM保证线程安全),但会造成内存资源的浪费。
我们可以创建一个这样的类:
1、定义私有化的成员变量:需初始化,用static修饰。
2、私有化构造器,防止其被其他类new。
3、对外提供公共方法,返回获取创建好的单例对象,用static修饰。
public class Singleton { // 私有化的成员变量:需初始化 private static Singleton singleton = new Singleton(); // 私有化构造器,防止其被其他类new private Singleton() { } // 对外提供公共方法,返回获取创建好的单例对象 public static Singleton getInstance() { return singleton; } public void otherMethod() { System.out.print("其他的行为方法"); } }
懒汉式是在第一次使用的时候,才将单例对象创建出来。该方式存在线程安全的问题,但不会造成内存资源的浪费。
我们可以创建一个这样的类:
1、定义私有化的成员变量:无需初始化,用static修饰。
2、私有化构造器,防止其被其他类new。
3、对外提供公共方法,返回获取创建好的单例对象。只有当不存在时候才new,存在则直接返回,用static修饰。
public class Singleton { // 私有化的成员变量:不做初始化 private static Singleton singleton = null; // 私有化构造器,防止其被其他类new private Singleton() { } // 对外提供公共方法,返回获取创建好的单例对象 public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } public void otherMethod() { System.out.print("其他的行为方法"); } }
懒汉式,怎么解决线程安全问题呢?
两个线程同时访问的时候,我们可以加锁处理。
1、私有化的成员变量:不做初始化,volatile 保证原子性。
2、私有化构造器,防止其被其他类new。
3、对外提供公共方法,返回获取创建好的单例对象。加两层非空校验,将第二层校验为null的代码块用synchronized同步代码块。
public class Singleton { // 私有化的成员变量:不做初始化 private volatile static Singleton singleton = null; // 私有化构造器,防止其被其他类new private Singleton() { } // 对外提供公共方法,返回获取创建好的单例对象 public static Singleton getInstance() { // 第一层非空校验 if (singleton == null) { // 加同步锁,保证只有一个线程进入 synchronized (Singleton.class) { // 第二层非空校验,防止在第一次非空校验时,两个线程拿到的都是null对象而创建两次。 if (singleton == null) { singleton = new Singleton(); } } } return singleton; } public void otherMethod() { System.out.print("其他的行为方法"); } }
可以了解下对象在JVM中的创建步骤。以及线程相关知识点,同步,怎么保证原子性等。
1、私有化构造器,防止其被其他类new。
2、使用内部类(JVM保证),创建单例对象。
3、对外提供公共方法,通过调用内部类的属性,返回获取的创建好的单例对象。
public class StaticSingleton { // 私有化构造器,防止其被其他类new private StaticSingleton() { } // 使用内部类(JVM保证),创建单例对象 private static class SingletonFactory { private static StaticSingleton singleton = new StaticSingleton(); } // 对外提供公共方法,通过调用内部类的属性,返回获取的创建好的单例对象 public static StaticSingleton getInstance() { return SingletonFactory.singleton; } public void otherMethod() { System.out.print("其他的行为方法"); } }
public enum SingletonEnum { INSTANCE; public void otherMethod() { System.out.print("其他的行为方法"); } }
问题:如何破坏单例?
1.反射,通过反射获取单例对象的构造器,暴力破解后即可创建多个不同实例。怎么防止:禁止通过反射会创建对象。
2.序列化,通过深克隆复制对象,可生成多个实例。怎么防止:重写在单例对象中readObject()方法。
原文:https://www.cnblogs.com/scorpio-cat/p/12707402.html