再战设计模式(一)之单例模式

关于设计模式,之前已经看过很多很多了.但是每次到过了很久之后,在此回忆总是感觉有模糊的感觉.这次我要重新学习一下设计模式.并在这里做一个博客记录,以作为以后的备忘吧.大家看过如果有问题或者更好的方式,可以在评论留言

这篇文章也是我写设计模式的开章.主要方式是看视频和看书,设计模式被人称为GOF23 主要就是国外有4个编程牛人编写的这23中设计模式.大家主要理解人家的思想.而不是死记硬背,这句话也是对我自己说的.

设计模式的分类

1.创建型模式:5种

单例模式 工厂模式 抽象工厂模式 建造者模式 原型模式

2.结构型模式 7种

适配模式 桥接模式 装饰模式 组合模式 外观模式 享元模式 代理模式

3.行为模式:11 种

模板方法模式 命令模式 迭代器模式 观察者模式 中介者模式 备忘录模式 解释器模式 状态模式 策略模式 职责链模式 访问者模式

分类主要是为了方便记忆 突然想到了jdk 8 有 9个函数式接口 想回忆下

单例模式的实现

单例模式-饿汗模式

  饿汉模式顾名思义就是非常饿,直接吃.其实关于单例模式的总结就一句话,私有构造方法,提供外部获取实例的方法

  所以在类加载的时候对象已经创建好了.方法没有锁,调用效率高!

/**
 * @Created by xiaodao
 */
public class Sington01 {

    //类初始化时立即加载对象(没有延迟加载的优势),天然的线程安全
    private static Sington01 instance = new Sington01();


    private Sington01() {
    }

    public static Sington01 getInstance(){
        return instance;
    }
}

延时加载模式

public class Sington02 {

    /***
     * 延时加载模式,在调用的时候,才会获取对像
     */
    private static Sington02 sington ;

    private Sington02() {
    }

    /**
     * 同步调用效率低
     * @return
     */
    public static Sington02 getInstance(){
        synchronized (Sington02.class){
            if(null == sington){
                sington= new Sington02();
            }
            return sington;
        }
    }
}

 双重检查 double-check单例模式

/**
 * @Created by xiaodao
 * 双重检索模式
 */
public class Sington03 {

    /***
     *
     */
    private static volatile  Sington03 sington ;

    private Sington03() {
    }

    /**
     *
     * @return
     */
    public static Sington03 getInstance(){
        if(sington !=null ){

        }else{
            synchronized (Sington03.class){
                if(sington== null){
                    sington = new Sington03();
                }
            }
        }
        return sington;

    }

    public static void main(String[] args) {
        Sington03 sington03 = getInstance();
        Sington03 sington04 = getInstance();
        System.out.println(sington03);
        System.out.println(sington04);
    }
}

静态内部类 延时加载模式

/**
 * @Created by xiaodao
 * 
 */
public class Sington04 {

    /***
     *
     */
    private static class Sington04Interior {
        private static Sington04 instance= new Sington04();
    };

    private Sington04() {
    }

    /**
     *
     * @return
     */
    public static Sington04 getInstance(){
        return Sington04Interior.instance;
    }

    public static void main(String[] args) {
        Sington04 sington03 = getInstance();
        Sington04 sington04 = getInstance();
        System.out.println(sington03);
        System.out.println(sington04);
    }
}
View Code

加载类的时候外部没有static属性不会像恶汉模式一样立即加载对象

只有getInstance才会加载静态内部类.是线程安全的

兼备了并发高效和延迟加载的优势

枚举方式实现单例模式

/**
 * @Created by xiaodao
 *
 */
public enum  Sington05 {

    /***
     * 定义一个枚举.代表singleton的一个实例
     */
    INSTANCE;
    public void singletonOperator(){
        //这个单例的功能处理
    }

    public static void main(String[] args) {
        Sington05 sington05 = Sington05.INSTANCE;
    }

}

枚举的单例,可以避免反射和反序列来破解,实现比较简单

 反射跳过单例

 public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        //第一次调用.
//        Sington06 instance = Sington06.getInstance();
//        System.out.println(instance);
        Class<Sington06> aClass = (Class<Sington06>) Class.forName("com.xiaodao.jdk8.disign.sington.Sington06");
        //获取构造器
        //第二次调用.
        Constructor<Sington06> declaredConstructor = aClass.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Sington06 s2 = declaredConstructor.newInstance();
        Sington06 s1 = declaredConstructor.newInstance();
        System.out.println(s1);
        System.out.println(s2);
        //第三次.
        Sington06 instance = Sington06.getInstance();
        System.out.println(instance);
        Sington06 s3 = declaredConstructor.newInstance();
    }

这种方式可以跳过单例模式,需要在单例模式里加一个判断.
private Sington06() {
if(sington !=null){
throw new RuntimeException();
}
}
 

平时我们写项目不需要这样的判断.但是如果是写个框架的话,可能需要严谨一点,但是这也是有漏洞的,如果你没有创建过一次单例,那么通过反射就是无数次调用,如果你创建了.那么就不能通过反射就创建了.

反序列化破解与防止反序列破解

 Sington06 instance = Sington06.getInstance();
        System.out.println(instance);

        FileOutputStream out = new FileOutputStream("/Users/xuyuanfang/study/java视频/disgin/a.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
        objectOutputStream.writeObject(instance);
        out.close();
        objectOutputStream.close();

        //反序列化

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/xuyuanfang/study/java视频/disgin/a.txt"));
        Sington06 s3 = (Sington06) objectInputStream.readObject();
        System.out.println(s3);

防止破解:

    /**
     * 反序列化 如果定义了readResolve方法 直接调用这个方法返回对象,不需要重新创建对象
     * @return
     */
    public Object readResolve() throws ObjectStreamException {
        return  sington;
    }

效率对比

恶汉模式,比懒汉方式在多线程环境下效率好点

如何选用呢?

如果对象占用资源少,不需要延时加载

枚举比恶汉式比较好

如果对象资源占用比较大的话.需要延时加载

静态内部类好于懒汉式 

原文地址:https://www.cnblogs.com/bj-xiaodao/p/10801232.html