设计模式(一:单例模式)

  1. 创建型模式: – 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
  2. • 结构型模式: – 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模 式。
  3. • 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模 式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

1.单例模式:
1):核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问
2):常见的五种单例模式实现方式 :
1.饿汉式(线程安全,调用效率高。 但是,不能延时加载。)
2.懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
3.双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)
4.静态内部类式(线程安全,调用效率高。 但是,可以延时加载)
5.枚举式(线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列 化漏洞!)
2.代码演示:
1.饿汉:

package com.example.shejimoshi.singleton;

/**
 * 单例模式之饿汉模式
 */
public class SingleTonDemo {

    //类初始化时立即加载(没有延时加载的优势)加载类时 天然是线程安全的
    private static SingleTonDemo s = new SingleTonDemo();
    //构造方法私有化
    private SingleTonDemo()
    {

    }
    //提供一个公开的外部访问点 调用效率高
    public static SingleTonDemo getS()
    {
        return s;
    }
}

2.懒汉式

package com.example.shejimoshi.singleton;

/**
 * 单例模式之懒汉式
 */
public class SingleTonDemo1 {


    //类初始化时 不初始化这个对象 延时加载 正在用的时候再创建 资源利用效率低
    private static SingleTonDemo1 s1;
    //构造方法私有化
    private SingleTonDemo1()
    {

    }
    //提供一个公开的外部访问点 方法同步 但是调用效率低 因为并发访问需要等待
    public static synchronized SingleTonDemo1 getS1()
    {
        if(s1==null)
        {
           s1= new SingleTonDemo1();
        }
        return s1;
    }
}

  1. 静态内部类式
package com.example.shejimoshi.singleton;

/**
 * 单例模式之静态内部类
 *兼备了并发高效调用和延迟加载的优势
 */
public class SingleTonDemo3 {


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

    }

    //静态内部类 加载类时是线程 安全的
    private static class SingletonClassInstance
    {

        private static final SingleTonDemo3 s =new SingleTonDemo3();
    }



    public static  SingleTonDemo3 getS3()
    {//只有真正调用getS3,才会加载静态内部类
        return SingletonClassInstance.s;
    }
}

  1. 枚举式:
package com.example.shejimoshi.singleton;

/**
 * 单例模式之枚举方式实现
 * 唯一的遗憾是没有懒加载 就是延时加载
 */
public enum  SingleTonDemo4 {
    //这个枚举元素 本身就是单例模式
    INSTENCE;

    /**
     * 添加自己需要的操作
     */
    public void singletonOperation()
    {

    }

}

3.如何选择:
在这里插入图片描述

4.使用反射跳过单例模式 (测试针对懒汉模式):

package com.example.shejimoshi.singleton;

import java.lang.reflect.Constructor;

/**
 * 测试使用反射破解单例
 */
public class Test1 {

    public static void main(String[] args) throws Exception {
          //反射
        Class<SingleTonDemo5> clazz=  (Class<SingleTonDemo5>)Class.forName("com.example.shejimoshi.singleton.SingleTonDemo5");
          Constructor<SingleTonDemo5> c= clazz.getDeclaredConstructor(null);
          //跳过认证 可以访问私有的方法
          c.setAccessible(true);
        SingleTonDemo5 s1= c.newInstance();
        SingleTonDemo5 s2= c.newInstance();
        System.out.println(s1);
        System.out.println(s2);
    }
}

执行结果:很显然通过反射没有走单例模式

在这里插入图片描述
5.单例模式怎么防止 使用反射跳过单例模式 (测试针对懒汉模式)

package com.example.shejimoshi.singleton;

/**
 * 单例模式之懒汉式(测试反射漏洞)
 */
public class SingleTonDemo5 {


    //类初始化时 不初始化这个对象 延时加载 正在用的时候再创建 资源利用效率低
    private static SingleTonDemo5 s1;
    //构造方法私有化
    private SingleTonDemo5()
    {
        //加上次代码可防止反射跳过 单例
      if (s1!=null)
      {
          throw new RuntimeException();
      }
    }
    //提供一个公开的外部访问点 方法同步 但是调用效率低 因为并发访问需要等待
    public static synchronized SingleTonDemo5 getS1()
    {
        if(s1==null)
        {
           s1= new SingleTonDemo5();
        }
        return s1;
    }
}

6.通过反序列化来创建多个对象来破解单例:

package com.example.shejimoshi.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
 * 测试使用反序列化破解单例
 */
public class Test2 {

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

        SingleTonDemo5 s1 =SingleTonDemo5.getS1();
        SingleTonDemo5 s2 =SingleTonDemo5.getS1();
        System.out.println(s1);
        System.out.println(s2);
        //使用反序列化来创建多个对象  (针对懒汉模式)
        //首先 要反序列化的类要实现 Serializable接口

        //把对象写入硬盘
        FileOutputStream fos =new FileOutputStream("d:/a.txt");
        ObjectOutputStream oos =new ObjectOutputStream(fos);
        oos.writeObject(s1); //把s1对象写入硬盘
        oos.close();
        fos.close();
        //从硬盘中读取
        ObjectInputStream ois =new ObjectInputStream(new FileInputStream("d:/a.txt"));
        SingleTonDemo5 s3 =(SingleTonDemo5) ois.readObject();
        System.out.println(s3);

        
    }
}

执行结果:显然反序列化后的对象改变了 违反了 单例
在这里插入图片描述
7.怎么防止反序列化 破解

package com.example.shejimoshi.singleton;

import java.io.Serializable;

/**
 * 单例模式之懒汉式(测试反射漏洞)
 */
public class SingleTonDemo5 implements Serializable {


    //类初始化时 不初始化这个对象 延时加载 正在用的时候再创建 资源利用效率低
    private static SingleTonDemo5 s1;
    //构造方法私有化
    private SingleTonDemo5()
    {
        //加上次代码可防止反射跳过 单例
      if (s1!=null)
      {
          throw new RuntimeException();
      }
    }
    //提供一个公开的外部访问点 方法同步 但是调用效率低 因为并发访问需要等待
    public static synchronized SingleTonDemo5 getS1()
    {
        if(s1==null)
        {
           s1= new SingleTonDemo5();
        }
        return s1;
    }

    //反序列化时 如果定义了次方法 直接返回此方法的对象 防止反序列化破解
    private Object readResolve()
    {
        return s1;
    }
}

执行结果:
duo'x
8.使用CountDownLatch同步辅助类测试效率:

package com.example.shejimoshi.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.concurrent.CountDownLatch;

/**
 * 测试多线程环境下各种环境的效率
 */
public class Test3 {

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

        //获取当前时间的时间戳
        long start =System.currentTimeMillis();

        int threadNum =10;
        CountDownLatch countDownLatch =new CountDownLatch(threadNum);
        for (int i=0;i<threadNum;i++)
        {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<100000;i++)
                    {
                        Object o =SingleTonDemo1.getS1();
                    }
                    //让CountDownLatch计数器减一
                    countDownLatch.countDown();
                }
            }).start();
        }

        //main线程阻 直到计数器变为0 才会继续往下继续
        countDownLatch.await();
        //获取当前时间的时间戳
        long end =System.currentTimeMillis();
        System.out.println("----"+(end-start));

    }
}

除了懒汉都还可以。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值