这里写目录标题
单例设计模式
懒汉模式 注释写的很清楚
代码实现
package com.luyi.designpattern;
/**
* 懒汉模式
* @author 卢意
* @create 2020-12-28 20:06
*/
public class LazySingletonTest {
public static void main(String[] args) {
// 最基础的单例模式
// LazySingleton instance = LazySingleton.getInstance();
// LazySingleton instance1 = LazySingleton.getInstance();
// System.out.println(instance == instance1); // TRUE
// 多线程环境下的问题
new Thread(()-> {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(()-> {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
// 多线程不是单例
// com.luyi.designpattern.LazySingleton@5e5035fb
//com.luyi.designpattern.LazySingleton@6a3289ca
// 构造函数加入 synchronized 字段 加锁 但是会有性能损耗
//com.luyi.designpattern.LazySingleton@5e5035fb
//com.luyi.designpattern.LazySingleton@5e5035fb
// 可以在 INSTANCE == null 时再加锁 减少不必要的锁
}
}
class LazySingleton {
// private static LazySingleton INSTANCE;
private volatile static LazySingleton INSTANCE; //加入volatile 字段 保证不会重排序
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (INSTANCE == null) {
synchronized (LazySingleton.class) {
if (INSTANCE == null) {
// 多判断一次 防止多线程情况 同时进入这个锁
INSTANCE = new LazySingleton();
// 声明对象时 在字节码层
// JIT(即时遍历) CPU 会指令顺序重排 单线程模式下 第2步和第三步可以颠倒 多线程情况下 如果先进行引用赋值 就会导致
// 未初始化就被赋值的 会导致空指针针 加入volatile 字段 保证不会重排序
// 1 分配空间
// 2 进行初始化
// 3 引用赋值
//
}
}
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
}
return INSTANCE;
}
}
饿汉模式
代码实现 利用jvm的类加载机制 保证实例的唯一性(比懒汉模式简单的多)
package com.luyi.designpattern;
/**
* 饿汉模式
* @author 卢意
* @create 2020-12-28 21:01
*/
public class HungrySingletonTest {
public static void main(String[] args) {
HungrySingleton instance = HungrySingleton.getInstance(); // 加载 HungrySingleton 的类加载机制
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance == instance2); //true
}
}
// 初始阶段就完成了类的初始实例化 本质上借助于jvm的类加载机制 保证实例的唯一性
// 类加载过程:
// 加载二进制数据到内存中 生成对应的class数据结构
// 连接 验证 准备(给类的静态成员变量符初值) 解析
// 初始化 : 给类的静态变量赋初值
class HungrySingleton {
private static HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
静态内部类
代码实现 也是通过jvm的类加载机制保证了线程的安全 但是是在使用的时候调用 不是创建类的时候调用
package com.luyi.designpattern;
import java.util.Stack;
/**静态内部类实现单例模式
* @author 卢意
* @create 2020-12-28 21:11
*/
public class InnerClassSingletonTest {
public static void main(String[] args) {
InnerClassSingleton instance = InnerClassSingleton.getInstance();
InnerClassSingleton instance2 = InnerClassSingleton.getInstance();
System.out.println(instance == instance2); // true
new Thread(()-> {
InnerClassSingleton instance3 = InnerClassSingleton.getInstance();
System.out.println(instance3);
}).start();
new Thread(()-> {
InnerClassSingleton instance4 = InnerClassSingleton.getInstance();
System.out.println(instance4);
}).start();
// 是线程安全的
//com.luyi.designpattern.InnerClassSingleton@74f84cf
//com.luyi.designpattern.InnerClassSingleton@74f84cf
}
}
// 也是依赖jvm的类加载机制来保证线程安全
class InnerClassSingleton {
private static class InnerClassHolder {
// 在调用getInstance() 时再会进行加载 (懒加载)
private static InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
private InnerClassSingleton() {
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.INSTANCE;
}
}
反射攻击
上面的模式在反射的情况下 还是会有风险
防止反射攻击
在初始化类的时候判断是否已经存在实例
private InnerClassSingleton() {
if (InnerClassHolder.INSTANCE != null) {
throw new RuntimeException(" 单例不允许多个实例 ");
}
}
枚举类型的单例实现 防止反射攻击 也是通过jvm的类加载机制 保证了代码的线程安全
package com.luyi.designpattern;
/**
* 枚举实现单例模式 防止反射攻击
* @author 卢意
* @create 2020-12-28 21:36
*/
public enum EnumSingleton {
INSTANCE;
public void print() {
System.out.println(this.hashCode());
}
}
// 看似代码量很少 但是jvm的操作很多 性能消耗比较大
class EnumTest {
public static void main(String[] args) {
EnumSingleton instance = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
System.out.println(instance == instance2); // true
}
}
序列化对单例模式的攻击
防止序列化攻击
注意要加版本号 不然会报错
运行时异常就是单例模式(饿汉模式)
spring的bean代理工厂有针对单例模式的变量
工厂方法模式
引入设计模式的前提就是 需要找到代码内的稳定的部分 和变化的部分 变化的过程中找的稳定的部分 (个人理解就是面向接口(抽象类)编程 比如’变化的过程中找的稳定的部分’ 就是重写接口(继承的抽象类)的方法 或者说是实现接口(继承抽象类)的类所公共的部分 代码复用…)
简单工厂 和 工厂方法模式 的区别
简单工厂并不是一种设计模式 而是一种编码方式
简单工厂的编码方式(编程习惯)
package com.luyi.designpattern;
/** 简单工厂模式
* @author 卢意
* @create 2020-12-29 20:28
*/
public class FactoryMethod {
public static void main(String[] args) {
Application application = new Application();
Product product = application.getObject("1");
product.method1();
}
}
class SimpleFactory {
public static Product createProduct(String type) {
if ("0".equals(type)) {
return new ProductA();
} else if ("1".equals(type)) {
return new Product1();
}
return null;
}
}
interface Product {
public void method1();
}
// 未使用设计模式的方案
//class ProductA {
// public void method1() {
// System.out.println("ProductA.method executed");
// }
//
//}
class ProductA implements Product {
@Override
public void method1() {
System.out.println("ProductA.method executed");
}
}
class Product1 implements Product {
@Override
public void method1() {
System.out.println("Product1.method executed");
}
}
class Application {
// private ProductA createProduct() {
// // ... init
// return new ProductA();
// }
private Product createProduct(String type) {
// ... init
return SimpleFactory.createProduct("1"); // 多态
}
Product getObject(String type) {
Product product = createProduct(type); // 多态
//..
return product;
}
}
工厂方法模式
工厂方法模式是一种设计模式 是将 产品具体实例化的过程推迟给其子类实现
符和开闭原则(对拓展开放 对修改关闭)
符和单一职责原则
package com.luyi.designpattern;
/**
* @author 卢意
* @create 2020-12-29 20:28
*/
public class FactoryMethod {
public static void main(String[] args) {
Application application = new ConcreateProductA();
Product product = application.getObject();
product.method1(); // res = ProductA.method executed
}
}
interface Product {
public void method1();
}
class ProductA implements Product {
@Override
public void method1() {
System.out.println("ProductA.method executed");
}
}
class Product1 implements Product {
@Override
public void method1() {
System.out.println("Product1.method executed");
}
}
abstract class Application {
// 创建对象是稳定的部分 不稳定的是具体的实现 (返回值) 说以符和设计模式的思想 将该类设置成抽象类 稳定的部分设置成抽象方法
abstract Product createProduct();
Product getObject() {
Product product = createProduct(); // 多态
// .. 不同的的处理
// .. 不同的的处理
return product;
}
}
class ConcreateProductA extends Application {
@Override
Product createProduct() {
// .. 不同的的处理
return new ProductA();
}
}
class ConcreateProduct1 extends Application {
@Override
Product createProduct() {
// .. 不同的的处理
return new Product1();
}
}
应用场景
抽象工厂模式
与工厂方法的区别是 在定义了一个 接口 整合了其他相关的接口 也可以说 抽象工厂模式是由一系列工厂方法模式组合而成的
package com.luyi.designpattern;
/**
* @author 卢意
* @create 2020-12-30 21:17
*/
public class AbstractFactory {
public static void main(String[] args) {
IDataBaseUtils iDataBaseUtils = new MysqlDataBaseUtils();
IConnection connection = iDataBaseUtils.getConnection();
connection.connect();
ICommand command = iDataBaseUtils.getCommand();
command.command();
}
}
// 如果模拟的是数据的操作 变化的部分 就是 mysql 或者 oracle
// 稳定的部分 连接connection 命令sql的执行command
interface IConnection {
void connect();
}
interface ICommand {
void command();
}
interface IDataBaseUtils {
IConnection getConnection();
ICommand getCommand();
}
class MysqlConnection implements IConnection {
@Override
public void connect() {
System.out.println("mysql connected");
}
}
class MySqlC