分类
总体来说设计模式分为三大类:
创建型模式
- 工厂方法模式:定义一个接口用于创建对象,但让子类决定实例化哪个类。
- 抽象工厂模式:提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。
- 单例模式:确保一个类只有一个实例,并提供全局访问点。
- 建造者模式:使用多个简单的对象一步步构建成一个复杂的对象。
- 原型模式:通过复制现有的实例来创建新的对象,而不是通过构造函数。
结构型模式
- 适配器模式:将一个类的接口转换成客户端期望的另一个接口。
- 装饰器模式:动态地给一个对象添加一些额外的职责,而不会影响到其他对象。
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问。
- 外观模式:为一组接口提供一个统一的高层接口,使子系统更易使用。
- 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- 组合模式:将对象组合成树形结构以表示“部分-整体”层次结构。
- 享元模式:使用共享对象来高效地支持大量细粒度的对象。
行为型模式
- 策略模式:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。
- 模板方法模式:定义一个操作中的算法的骨架,将一些步骤延迟到子类中。
- 观察者模式:定义一种依赖关系,让多个观察者对象同时监听某一个主题对象。
- 迭代子模式:提供一种方法顺序访问一个集合对象中的各个元素,而无需暴露该对象的内部表示。
- 责任链模式:通过将请求传递给链中的多个处理者,直到有一个处理者处理这个请求。
- 命令模式:将请求封装成一个对象,以便使用不同的请求、队列或日志请求,并支持可撤销操作。
- 备忘录模式:在不暴露对象内部状态的情况下,保存和恢复对象的状态。
- 状态模式:允许一个对象在其内部状态改变时改变它的行为。
- 访问者模式:表示一个作用于某对象结构中的各个元素的操作,它使你可以在不改变这些元素的类的前提下定义作用于这些元素的新操作。
- 中介者模式:定义一个对象,用来封装一系列对象之间的交互,使得这些对象不需要直接交互。
- 解释器模式:给定一个语言,定义它的文法,并解释句子。
代码案例
单例模式
确保一个类只有一个实例,并提供全局访问点
饿汉模式
说明
在类创建的时候就开始实例化对象;不管用没用到,一开始就创建。
步骤
- 创建静态对象,对象直接实例化
- 私有化构造方法
- 提供获取实例的方法,返回实例
代码
public class Eager {
// 类创建时候立即创建实例 ,且实例不可修改
private static final Eager eager = new Eager();
// 私有化构造器
private Eager() {}
// 对外暴露获取实例的方法
public static Eager getInstance() {
return eager; // 永远返回的都是同一个对象,此对象在类加载的时候就创建了。
}
public static void main(String[] args) {
// 创建实例
Eager eager1 = Eager.getInstance(); // 通过getInstance()方法获取实例
Eager eager2 = new Eager(); // 创建新实例
Eager eager3 = Eager.getInstance(); // 通过getInstance()方法获取实例
System.out.println("eager1 等于 eager2 : "+(eager1 == eager2));// 判断eager1 和 eager2 是否相等。结果:false
System.out.println("eager1 等于 eager3 : "+(eager1 == eager3));// 判断eager1 和 eager3 是否相等。结果:true 。 说明通过getInstance()获取到的对象是同一个
System.out.println("eager2 等于 eager3 : "+(eager2 == eager3));// 判断eager2 和 eager3 是否相等。结果:false
System.out.println();
// 打印对象的内存地址
System.out.println("eager1内存地址: "+eager1.hashCode());
System.out.println("eager2内存地址: "+eager2.hashCode());
System.out.println("eager3内存地址: "+eager3.hashCode());
}
}
运行结果
eager1 等于 eager2 : false
eager1 等于 eager3 : true
eager2 等于 eager3 : false
eager1内存地址: 125130493
eager2内存地址: 914504136
eager3内存地址: 125130493
懒汉模式
说明
类加载时不实例化,调用时才实例化。
步骤
- 创建静态对象,对象直接实例化
- 私有化构造方法
- 提供获取实例的方法,判断对象是否为空,为空则实例化。实例化之前加锁,保证线程安全。返回实例。
代码
public class Lazy {
// 初始化不实例化
private volatile static Lazy lazy;
// 私有化构造方法
private Lazy() {
}
// 对外开放获取实例的方法
public static Lazy getInstance() {
if (lazy == null){
// 防止并发时创建多个实例。在创建时加锁。只有一个线程能获取到锁,其他线程会等待。只要其中一个线程把对象实例化后,之后其他线程就能直接获取到对象实例,不会再实例化新对象。
synchronized (Lazy.class){
lazy = new Lazy();
}
}
return lazy;
}
public static void main(String[] args) {
// 创建实例
Lazy lazy1 = Lazy.getInstance(); // 通过getInstance()方法获取实例
Lazy lazy2 = new Lazy(); // 创建新实例
Lazy lazy3 = Lazy.getInstance(); // 通过getInstance()方法获取实例
System.out.println("lazy1 等于 lazy2 : "+(lazy1 == lazy2));// 判断lazy1 和 lazy2 是否相等。结果:false
System.out.println("lazy1 等于 lazy3 : "+(lazy1 == lazy3));// 判断lazy1 和 lazy3 是否相等。结果:true 。 说明通过getInstance()获取到的对象是同一个
System.out.println("lazy2 等于 lazy3 : "+(lazy2 == lazy3));// 判断lazy2 和 lazy3 是否相等。结果:false
System.out.println();
// 打印对象的内存地址
System.out.println("lazy1内存地址: "+lazy1.hashCode());
System.out.println("lazy2内存地址: "+lazy2.hashCode());
System.out.println("lazy3内存地址: "+lazy3.hashCode());
}
}
运行结果
lazy1 等于 lazy2 : false
lazy1 等于 lazy3 : true
lazy2 等于 lazy3 : false
lazy1内存地址: 125130493
lazy2内存地址: 914504136
lazy3内存地址: 125130493
原型模式
通过复制现有的实例来创建新的对象,而不是通过构造函数。
步骤
- 创建一个实体类,实现Cloneable接口
- 重写clone方法
代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Prototype implements Cloneable {
private String name; // 实体属性
private int age; // 实体属性
// 如果属性为对象,则对象也需要实现Cloneable接口
private Student student; // 实体属性-对象
// 重写clone方法,返回克隆的新对象,新对象类型强制转换成Prototype
@Override
protected Prototype clone(){
Prototype clone = null;
try {
clone = (Prototype) super.clone();
clone.setStudent(this.student.clone()); // 对实体属性进行深度克隆
} catch (CloneNotSupportedException e) {
System.out.println("Prototype 对象克隆失败");
throw new RuntimeException(e);
}
return clone;
}
}
// 主要用于给Prototype做对象属性
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student implements Cloneable {
private String studentNumber;
@Override
protected Student clone() {
try {
return (Student) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Student 对象克隆失败");
throw new RuntimeException(e);
}
}
}
// 测试方法
class Test {
public static void main(String[] args) {
// 创建一个原始对象
Prototype prototype = new Prototype("张三", 18, new Student("0000"));
System.out.println("原始对象: \n\t " + prototype + " \n\t 原始对象的地址:"+ prototype.hashCode());
System.out.println();
// 克隆原始对象
Prototype clone = prototype.clone();
System.out.println("克隆对象: \n\t " + clone + " \n\t 克隆对象的地址:"+ clone.hashCode());
System.out.println();
System.out.println("经过克隆后原始对象和克隆对象数据:\n\t原始对象:"+ prototype + "\n\t克隆对象:" + clone);
System.out.println("\n\t原始对象内存地址:"+ prototype.hashCode() + "\n\t克隆对象内存地址:" + clone.hashCode());
System.out.println();
System.out.println("原始对象实体Student属性的内存地址:"+prototype.getStudent().hashCode());
System.out.println("克隆对象未覆盖原始实体Student属性时,实体属性的内存地址:"+clone.getStudent().hashCode());
System.out.println();
clone.setName("李四");
clone.setAge(20);
clone.getStudent().setStudentNumber("33333");
System.out.println("克隆对象覆盖原始实体属性后:\n\t原始对象:"+ prototype + "\n\t克隆对象:" + clone);
System.out.println("\n\t原始对象内存地址:"+ prototype.hashCode() + "\n\t克隆对象内存地址:" + clone.hashCode());
System.out.println();
System.out.println("原始对象实体Student属性的内存地址:"+prototype.getStudent().hashCode());
System.out.println("克隆对象覆盖原始实体Student属性后,实体属性的内存地址:"+clone.getStudent().hashCode());
System.out.println();
}
}
运行结果
原始对象:
Prototype(name=张三, age=18, student=Student(studentNumber=0000))
原始对象的地址:47464179
克隆对象:
Prototype(name=张三, age=18, student=Student(studentNumber=0000))
克隆对象的地址:47464179
经过克隆后原始对象和克隆对象数据:
原始对象:Prototype(name=张三, age=18, student=Student(studentNumber=0000))
克隆对象:Prototype(name=张三, age=18, student=Student(studentNumber=0000))
原始对象内存地址:47464179
克隆对象内存地址:47464179
原始对象实体Student属性的内存地址:1477691
克隆对象未覆盖原始实体Student属性时,实体属性的内存地址:1477691
克隆对象覆盖原始实体属性后:
原始对象:Prototype(name=张三, age=18, student=Student(studentNumber=0000))
克隆对象:Prototype(name=李四, age=20, student=Student(studentNumber=33333))
原始对象内存地址:47464179
克隆对象内存地址:98626212
原始对象实体Student属性的内存地址:1477691
克隆对象覆盖原始实体Student属性后,实体属性的内存地址:48669614