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

2019-04-14 18:40发布

  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 clazz= (Class)Class.forName("com.example.shejimoshi.singleton.SingleTonDemo5"); Constructor 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 除了懒汉都还可以。