java 单例模式的几种实现方式

1.懒汉式

public class Singleton {
    //2.本类内部创建对象实例
    private static Singleton instance = null;

    /**
         * 1.构造方法私有化,外部不能new
         */
    private Singleton() {

    }

//3.提供一个公有的静态方法,返回实例对象

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

单例模式的懒汉式体现了缓存的思想,延时加载就是一开始不要加载资源或者数据,一直等,等到马上就要使用这个资源的或者数据了,躲不过去了才去加载。

懒汉式是定性的时间换空间,不加同步的懒汉式是线程不安全的。

如何实现线程安全的懒汉式(双重检查加锁):

public class Singleton {
    private volatile static Singleton instance = null;

    // 私有化构造方法
    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }

        }
        return instance;
    }

}

2.饿汉式

public class Singleton {
    private static Singleton instance = new Singleton();

    // 私有化构造方法
    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }

}

饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要判断了,节省了运行时间。

3.使用静态内部类实现单例模式

public class Singleton {
    private static class SingletonHoler {
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHoler.instance;
    }

}

当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

4.使用枚举来实现单例

public enum Singleton {
    uniqueInstance;// 定义一个枚举的元素,它 就代表了Singleton的一个实例

    public void singletonOperation() {
        // 功能处理
        System.err.println("功能处理");
    }

}

5.破坏单例模式的三种方式

  • 反射
  • 序列化
  • 克隆
/**
 * 序列化对单例的破坏
 * @author Administrator
 *
 */
public class SingletonTest09 {

    public static void main(String[] args) throws Exception{

        System.out.println("-----------序列化----------------------");
         Singleton originSingleton = Singleton.getInstance();
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(bos);
          oos.writeObject(Singleton.getInstance());
          ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bis);
          Singleton serializeSingleton = (Singleton) ois.readObject();
          System.out.println(originSingleton == serializeSingleton);//false

          System.out.println("-----------反射----------------------");
          //通过反射获取
            Constructor<Singleton> cons = Singleton.class.getDeclaredConstructor();
            cons.setAccessible(true);
            Singleton reflextSingleton = cons.newInstance();
            System.out.println(reflextSingleton == originSingleton);//false

            System.out.println("---------------------------克隆----------------------");

            Singleton cloneSingleton = (Singleton) originSingleton.clone();
            System.out.println(cloneSingleton == originSingleton);//false

    }

    private static class Singleton  implements Serializable,Cloneable{
        /**
         * 1.构造方法私有化,外部不能new
         */
        private Singleton() {}


        //2.本类内部创建对象实例
        private static  volatile  Singleton instance;


        //3.提供一个公有的静态方法,返回实例对象
        public static  Singleton getInstance() {
            if(instance == null) {
                synchronized (Singleton.class) {
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }

        @Override
         protected Object clone() throws CloneNotSupportedException {
             return super.clone();
          }

    }

}

解决方案如下:

1、防止反射

定义一个全局变量,当第二次创建的时候抛出异常

2、防止克隆破坏

重写clone(),直接返回单例对象

3、防止序列化破坏

添加readResolve(),返回Object对象

代码如下:

**
 * 序列化对单例的破坏,解决方案
 * @author Administrator
 *
 */
public class SingletonTest10 {

    public static void main(String[] args) throws Exception{

        System.out.println("-----------序列化----------------------");
         Singleton originSingleton = Singleton.getInstance();
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(bos);
          oos.writeObject(Singleton.getInstance());
          ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bis);
          Singleton serializeSingleton = (Singleton) ois.readObject();
          System.out.println(originSingleton == serializeSingleton);//false

          System.out.println("-----------反射----------------------");
          //通过反射获取
            Constructor<Singleton> cons = Singleton.class.getDeclaredConstructor();
            cons.setAccessible(true);
            Singleton reflextSingleton = cons.newInstance();
            System.out.println(reflextSingleton == originSingleton);//false

            System.out.println("---------------------------克隆----------------------");

            Singleton cloneSingleton = (Singleton) originSingleton.clone();
            System.out.println(cloneSingleton == originSingleton);//false

    }

    private static class Singleton  implements Serializable,Cloneable{

        private static volatile boolean isCreate = false;//默认是第一次创建
        /**
         * 1.构造方法私有化,外部不能new
         */
        private Singleton() {
            if(isCreate) {
                throw new RuntimeException("已然被实例化一次,不能在实例化");
            }
            isCreate = true;
        }


        //2.本类内部创建对象实例
        private static  volatile  Singleton instance;


        //3.提供一个公有的静态方法,返回实例对象
        public static  Singleton getInstance() {
            if(instance == null) {
                synchronized (Singleton.class) {
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }

        @Override
         protected Object clone() throws CloneNotSupportedException {
             return instance;
         }

        /**
         * 防止序列化破环
         * @return
         */
        private Object readResolve() {
            return instance;
        }

    }

}

6.单例模式注意事项和细节说明

1) 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
2) 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new。
3) 单例模式 使用的场景:需要 频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级 对象),但又经常用到的对象、 工具类对象、频繁访问数据库或文件的对象(比如 数据源、session 工厂等)。

java 单例模式的几种实现方式
滚动到顶部