单例模式 懒加载终极写法 静态内部类写法

话不多说,先上代码

public class LazySingleton {

    private LazySingleton (){
        if(LazyHandler.LAZY!=null){
            throw new RuntimeException("单例模式不允许出现多例");
        }
    }

    private static class LazyHandler{
        private static final LazySingleton LAZY  = new LazySingleton();
    }

    public static final LazySingleton getInstance() {
        return LazyHandler.LAZY;
    }

}

  这个便是查了好多资料后总结出来的终极懒加载单例模式;

  先一步一步说一下懒加载单例模式的一步步进化写法,在过程中了解终极写法的优势

最初版

public class LazySingleton {

    private LazySingleton (){}
    private static  LazySingleton lazySingleton;


    public static LazySingleton getInstance() {
        if(lazySingleton!=null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

}

  最初版代码思想就是单例模式的基本思想操作:

   1、构造函数私有化;2、创建一个私有的静态属性保证全代码世界只有这一个单例;3、创建一个静态的getInstance来实现外部调用;

  缺点:因为是最初版,缺点还是很好找,线程不安全,一个线程进入if,另一个也进入if,那么就出现了俩不同实例。

进阶版

public class LazySingleton {

    private LazySingleton (){}
    private static  LazySingleton lazySingleton;


    public static LazySingleton getInstance() {
        if(lazySingleton!=null) {
            synchronized (LazySingleton.class) {
                if(lazySingleton!=null){
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }

}

  既然是进阶版,自然是解决掉了基础版的线程安全问题了,当然也有不同的写法,比如说 synchronized 直接加在getInstance方法上,但是思路就是一条,那就是加锁,处理掉线程安全问题。

  缺点:如果有点小缺点的话,就是有可能在锁的时候,造成阻塞,影响性能了。毕竟第一个在用,还把门锁上了,第二个自然就只能再门外瑟瑟发抖等前人用完。

终极版

public class LazySingleton {

    private LazySingleton (){
        if(LazyHandler.LAZY!=null){
            throw new RuntimeException("单例模式不允许出现多例");
        }
    }

    private static class LazyHandler{
        private static final LazySingleton LAZY  = new LazySingleton();
    }

    public static final LazySingleton getInstance() {
        return LazyHandler.LAZY;
    }

}

终极版目前来说就是最优解了。原理解析如下(其实就是用另类的饿汉式解决了懒汉式)

  1、怎么实现了单例:单例通过静态实现,原理相当于是结合了内部类和饿汉式。

  2、怎么实现了懒汉式:在java的类加载时候,内部类是不加在的,直到使用内部类的时候才会加载并初始化

  3、怎么解决掉多线程的安全问题:静态类的初始化是又jvm加载类时候初始化完成的,不会有多线程的问题,原理等同于饿汉式

  4、为什么私有的构造函数要加一个判定:如果通过反射的形式生成的LazySingleton ,破坏了单例,所以在私有构造函数上保证只能一个。

 

此处介绍两种   基本没见过也没什么用的破坏单例方法

  一、反射破坏

    public static void main(String[] args) {

        try {
            LazySingleton hsl = LazySingleton.getInstance();
            
            Class cla = LazySingleton.class;
            Constructor con = cla.getDeclaredConstructor();
            con.setAccessible(true);

            Object o = (LazySingleton)con.newInstance();
            System.out.println(o);
            System.out.println(hsl);
            System.out.println(o == hsl);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

  通过反射获取私有构造函数,给权限,然后强行破坏单例,生成多例。在终极版里边通过私有构造抛异常解决。

二、文件写入写出破坏


    public static void main(String[] args) {


        LazySingleton h = null;
        LazySingleton h1 = LazySingleton.getInstance();

        FileOutputStream fos = null;

        try {
            fos = new FileOutputStream("LazySingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(h1);
            oos.flush();
            oos.close();
            fos.close();

            FileInputStream fis = new FileInputStream("LazySingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            h = (LazySingleton) ois.readObject();
            ois.close();
            fis.close();

            System.out.println(h);
            System.out.println(h1);
            System.out.println(h == h1);


        } catch (Exception e) {
            e.printStackTrace();
        }


    }

  如果要使用这个测试记得实现 Serializable 接口 ,看到这里 运行一下就会发现其实 最终版是没有应对这个的方法的,解决的代码如下

最最终极补全加强版

public class LazySingleton implements Serializable {

    private LazySingleton (){
        if(LazyHandler.LAZY!=null){
            throw new RuntimeException("单例模式不允许出现多例");
        }
    }

    private static class LazyHandler{
        private static final LazySingleton LAZY  = new LazySingleton();

    }
    private Object readResolve(){
        return LazyHandler.LAZY;
    }
    public static final LazySingleton getInstance() {
        return LazyHandler.LAZY;
    }

}

  多了一个readResolve的方法,文件的写入写出方法是 readObject 方法,跟一下代码就发现,如果类有readResolve 方法,即使实例化了对象也会调用readResolve 覆盖掉新生成的对象,实现单例。

你可能感兴趣的:(单例模式,懒加载)