目录
一、单例模式
二、工厂模式
三、 抽象工厂模式
四、适配器模式
五、策略模式
六、装饰器模式
编辑
考点:会挖空super(coffeOpertion);
七、代理模式
为什么必须要使用代理对象?
和装饰器模式的区别
八、备忘录模式
一、单例模式
这个模式能确保一个类只有一个实例,并提供全局访问点,非常适合配置管理、日志对象等场景。
我们不使用单例模式的时候会导致
内存浪费(重复加载配置)
配置不一致(不同实例可能读取不同状态)
以下是不使用单例模式的例子
public class Printer { // 构造函数,用于初始化打印机对象时打印提示信息 public Printer() { System.out.println("这是一台普通的打印机"); } public static void main(String[] args) { // 创建第一个打印机对象 Printer printer1 = new Printer(); // 创建第二个打印机对象 Printer printer2 = new Printer(); // 判断两个打印机对象是否为同一实例,并输出结果 System.out.println("printer1和printer2是同一台吗?" + (printer1 == printer2)); } }
运行结果
这是使用单例模式的结果:
知识点扫盲:
 //    互斥访问 使用关键字synchronized 意思是打印完第一个,才能打印第二个,保证有序
public class Printer1 {
private static Printer1 instance;
private Printer1(){
    System.out.println("全公司唯一一台打印机已经启动");
}
private static synchronized Printer1 getInstance(){
    if (instance==null){
        instance = new Printer1();
    }
    return instance;
}
public void PrintDocument(String employee,String document){
    System.out.println(employee+"正在打印"+document);
}
public static void main(String[] args) {
//    员工A申请使用打印机
    Printer1 printerA = Printer1.getInstance();
//    员工B申请使用打印机
    Printer1 printerB= Printer1.getInstance();
//    检查是否是同一台打印机
    System.out.println("printerA和printerB是同一台吗?"+(printerA==printerB));
}
}
true
二、工厂模式
假设你开了一家奶茶店,顾客可以点不同口味的奶茶(原味、芒果、草莓)。
 问题:如果直接在代码里用new创建每种奶茶,会导致:
-  代码臃肿(每次新增口味都要改多处逻辑) 
-  难以维护(制作流程分散在各处) 
解决方案:用工厂模式统一管理奶茶的创建过程!
首先我们先扫盲一下接下来代码的知识点

package org.factory;
public class Case {
    public static void main(String[] args) {
        String str1="Hello";
        String str2="hello";
        String str3="World";
        boolean result1 =str1.equals(str2);
        boolean result2 =str1.equalsIgnoreCase(str2);
        System.out.println(result1);  // 输出false
        System.out.println(result2);  // 输出true
    }
}
equalsIgnoreCase() 是 Java 中 String 类的一个方法,用于比较两个字符串的内容是否相同,忽略大小写差异。
首先,先来梳理一下代码思路。首先定义一个接口,将制作奶茶的步骤在这个接口里。
之后创建不同的类实现先创建的接口,表示某种奶茶的具体制作过程。
之后创建工厂类,在工厂类里,客人要点什么奶茶就做什么奶茶。
最后,客人点单,具体喝什么奶茶就使用什么方法的调用。也算是做到了一个客户与程序之间的交互。
可能有人会疑问,为什么要写接口,为什么要写不同的奶茶类,试想,如果我们把所有实现奶茶的方法放在一个类里,让客户去从一个类里调用,判断条件会变多,代码也会变得很长,我们在寻找这些条件的时候,运行速度可能会变慢。所以当我们想要添加新口味时,前者需要修改同一个类,而后者只需扩展,不会影响已有代码。
这里也运用了多态的思想,客户点奶茶时,无论奶茶是原味、芒果还是其他类型,客户端都通过同一个接口方法(如prepare())操作。本质就是,不同奶茶类(Original、Mango)对这些方法有不同的实现。
经常面试题里会出现多态这样的提问,我以前会说,就像水,可以是冰,可以是气。现在我明白了,就像奶茶有不同的口味,就像王者荣耀的英雄同样是攻击却有不同的伤害技能,可以是物理攻击可以是法术攻击。
接下来我们来具体实现,观察工厂模式的作用。
至少需要5个文件来实现。
1.定义同一种类的行为模式
package org.factory;
public interface MilkTea {
    void prepare();//准备材料
    void make();//制作奶茶
    void pack();//打包
//这里介绍了奶茶的制作过程
}
2.具体类里是做什么
我们实现接口,注意:接口是实现,类是继承
这是经典珍珠奶茶
package org.factory;
public class OriginalMilkTea implements MilkTea{
//    代码实现流程:
    /**
     * 1.创建接口
     * 2.实现接口
     * 3.创建工厂类,在工厂中实现制造奶茶
     * 4.卖给客户不同的奶茶
     * 我们可以创建很多类似的类去表示不同奶茶的制作方式
     */
    @Override
    public void prepare() {
//    如果我要做珍珠奶茶 珍珠、红茶、牛奶
        System.out.println("正在准备材料");
        System.out.println("准备好材料:珍珠、红茶、牛奶");
    }
    @Override
    public void make() {
        System.out.println("正在制作中");
    }
    @Override
    public void pack() {
        System.out.println("用经典杯打包原味奶茶!");
    }
}
这是红糖姜茶
package org.factory;
public class RedSugarTea implements MilkTea{
    /*此类我表示的是红糖姜茶*/
    @Override
    public void prepare() {
        System.out.println("准备材料!");
        System.out.println("红糖、生姜、红茶");
    }
    @Override
    public void make() {
        System.out.println("混合熬煮中!");
    }
    @Override
    public void pack() {
        System.out.println("使用夏季限定包装杯包装!");
    }
    /**/
}
3.工厂模式来进行判断生产
package org.factory;
public class MilkFactory {
//    工厂里面制作奶茶
//    客人需要什么奶茶我就制作什么奶茶,所以需要我们定义一个函数
    public static MilkTea createTea (String type){
        if ("Orginal".equalsIgnoreCase(type)){
            return new OriginalMilkTea();
        }else if ("RedSugar".equalsIgnoreCase(type)){
            return new RedSugarTea();
        }
        throw new IllegalArgumentException("没有这种口味的奶茶");
    }
}
4.客户需要去定义 这里也相当于一个测试类 假如需要喝红糖姜茶
package org.factory;
public class Customer {
    public static void main(String[] args) {
        MilkTea RedSugar =MilkFactory.createTea("RedSugar");
        RedSugar.prepare();
        RedSugar.make();
        RedSugar.pack();
    }
}
接口就是一个数据类型,后面是一个对象,用对象去调用接口里面的方法,再识别出需要去调用哪个方法。
三、 抽象工厂模式

生活实例:苹果 vs 小米全家桶
假设你要购买一套电子产品,包含 手机 和 耳机。不同品牌(如苹果、小米)的产品风格不同,且手机和耳机需要配套使用(比如苹果手机和AirPods配对更佳)。
 抽象工厂模式 就是用来生产 同一品牌系列产品 的解决方案,确保你获得的手机和耳机是同一品牌风格,避免混搭不兼容。
上一个我们讲的是制作奶茶的过程,但是只是同一款产品,那就是奶茶。
但是在此设计模式中,可以同时制造耳机和手机,只需要多设计一些接口和实现类。生产 多个相关联的产品(比如同时生产手机+耳机+手表,且保证同一品牌)。
四、适配器模式
顾名思义,适配器模式主要用于解决接口不兼容的问题,让原本无法一起工作的类可以协同工作。生活中常见的例子是电源适配器,比如不同国家的插头标准不同,适配器可以让不同插头在同一个插座上使用。
其实我觉得这个虽然好理解,但是我感觉代码是不好理解的。
本质是功能不同的两个类,转为功能相同的一个类。
实际例子:现在有英语导游给中国人导游 为了让中国人听得懂外语 , 发明了耳机适配器.
代码实现:先明确输出的是中文,英文是英语导游说的,适配器是将英语导游的话转换为中文。
1.定义一个接口
package org.adapter;
public interface ChineseSpeaker {
//    我们是将英文翻译成中文 这里定义的是说中文的能力
    String ChineseAbility();
}
2.英语导游说英语
package org.adapter;
public class EnglishGuide {
    public String SpeakEnglish(){
        return "Hello !This is a GreatWall";
    }
}
3.,首先让值传入翻译器中,翻译之后,传出来
package org.adapter;
public class Translator implements ChineseSpeaker{
    private EnglishGuide guide;
    public Translator(EnglishGuide guide){
        this.guide=guide;
    }
//   翻译功能,要先传入英文,再让其转换为中文
    @Override
    public String ChineseAbility() {
        String message = guide.SpeakEnglish();
// replace原来的值    想要替换的值
        return message.replace("Hello !This is a","你好!这里是");
//        System.out.println(replace(message,"Hi,这里是"));
    }
}
4.给翻译器传入值,调用接口实现的函数
package org.adapter;
public class Test {
    public static void main(String[] args) {
        EnglishGuide englishGuide = new EnglishGuide();
        Translator translator = new Translator(englishGuide);
        System.out.println(translator.ChineseAbility());
    }
}
五、策略模式
这个模式很好理解,面对同一个类有不同的策略,假如你想买车,不考虑预算,你想买什么车就买什么车,可以买白色宝马,灰色宝马,黑色宝马,黑色奔驰,灰色奔驰,白色小米,黄色小米,等等。任意搭配。我们把所有能够组成的策略的类都生成出来。代码文件很多。
需要定义的东西很多,我们需要把不同属性作为接口抽出来,比如颜色,品牌,油型。

假如我们想要买白色特斯拉用98的油型。就是这样的代码。
package org.strategy;
public abstract class Car {
//    Car  颜色、油、品牌
    protected Color color;
    protected Soil soil;
    protected Brand brand;
    protected void info() {
        String info = color.color() + brand.Brand() + ": " + soil.soil();
        System.out.println(info);
    }
}
package org.strategy;
public class tebaiba extends Car{
    public tebaiba() {
        color = new WhiteImp();
        brand = new Tesila();
        soil = new Ninety_eight();
    }
}
package org.strategy;
public class Test {
    public static void main(String[] args) {
        Car tebaiba = new tebaiba();
        tebaiba.info();
    }
}
 White特斯拉: 98
在考试的时候最有可能挖空就是挖

第一个空一般都是接口或者抽象类。
六、装饰器模式
我使用了DeepSeek预测,今年最可能考的就是装饰器模式和代理模式。
不修改原有对象,只是将原有对象进行加工。
这里我们使用(咖啡加料系统)来进行学习。
假如普通咖啡10元,不加糖。假如我们这里有两种操作。
A类咖啡:我们需要加椰果来增加口感,价格就变成了12元。
B类咖啡:我们需要加牛奶来增加醇香,价格就变成了15元。
1. 接口类
package org.Decorator;
public interface CoffeOpertion {
//    1.对咖啡进行加料
    String addSugar();
//    2.对咖啡进行加价
    double price(double money);
}
2. 实现普通咖啡类
package org.Decorator;
public class SimpleCoffe implements CoffeOpertion{
//    我们首先要知道普通咖啡是什么规格,再进行装饰
    @Override
    public String addSugar() {
        return "无糖";
    }
    @Override
    public double price(double money) {
        return 10.0;
    }
}
3. 抽象装饰类
package org.Decorator;
//抽象装饰类
public abstract class CoffeeDecorator implements CoffeOpertion {
    protected CoffeOpertion coffeOpertion;
//    这个是装饰类,你要把装饰的对象传进来
    public CoffeeDecorator(CoffeOpertion coffeOpertion){
        this.coffeOpertion=coffeOpertion;
    }
    
}
4. A操作
package org.Decorator;
public class CoffeDecoratorA extends CoffeeDecorator{
    public CoffeDecoratorA(CoffeOpertion coffeOpertion) {
        super(coffeOpertion);
    }
    @Override
    public String addSugar() {
        return "A类咖啡饮品,额外加椰果";
    }
    @Override
    public double price(double money) {
        money = money+2;
        return money;
    }
}
5.B操作
package org.Decorator;
public class CoffeDecoratorB extends CoffeeDecorator{
    public CoffeDecoratorB(CoffeOpertion coffeOpertion) {
        super(coffeOpertion);
    }
    @Override
    public String addSugar() {
        return "拿铁咖啡,额外加牛奶";
    }
    @Override
    public double price(double money) {
        money =money+5;
        return money;
    }
}
6. 实现类
package org.Decorator;
public class Coffeshop extends SimpleCoffe{
    public static void main(String[] args) {
        SimpleCoffe simple = new SimpleCoffe();
        String sugar=simple.addSugar();
        double money = simple.price(10);
        System.out.println("这是普通咖啡的规格:"+sugar + ",价格" + money);
        CoffeOpertion coffeOpertionA= new CoffeDecoratorA(new SimpleCoffe());
        CoffeDecoratorB coffeDecoratorB = new CoffeDecoratorB(new SimpleCoffe());
        System.out.println(coffeOpertionA.addSugar()+"价格为"+coffeOpertionA.price(10));
        System.out.println(coffeDecoratorB.addSugar()+"价格为"+coffeDecoratorB.price(10));
    }
}
这是运行结果
考点:会挖空super(coffeOpertion);
在子类的构造函数里一定要强调父类。
    public CoffeDecoratorA(CoffeOpertion coffeOpertion) {
        super(coffeOpertion);
//        this.coffeOpertion=coffeOpertion;
    }七、代理模式
为什么必须要使用代理对象?
1.隐藏复杂性 :代理对象可以控制何时、如何创建真实对象(例如延迟加载)
2.功能扩展:代理可以在不修改真实对象的前提下,添加额外逻辑(缓存、权限验证)
3.接口透明:客户端无需知道背后是代理还是真实对象,只需调用统一接口
和装饰器模式的区别
-  代理:控制访问,侧重隐藏真实对象(如权限控制)。 
-  装饰器:动态扩展功能,侧重增强(如咖啡加糖)。 
代码实现:一个真实的类,一个代理真实的类,一个抽象的接口,代理与真实的类拥有相同的功能。在特定条件下被实现,比如,老师今天感冒不能上课就由课代表代表上课。
2
1.
package org.Proxy;
public interface Lesson {
    String CanLesson();
}
2.
package org.Proxy;
public class RealTeacher implements Lesson{
    @Override
    public String CanLesson() {
        return "老师正在朗读荷塘月色";
    }
}
3.
package org.Proxy;
public class StudentProxy implements Lesson{
//    关键:持有真实对象的引用
    protected RealTeacher teacher;
//    构造函数
    public StudentProxy(){
        this.teacher = new RealTeacher();
    }
//    添加检查权限,没有老师的时候学生上课
    public boolean checkPermission(){
        return false;
    }
    @Override
    public String CanLesson() {
        // 添加代理逻辑(示例:权限校验)
        if (checkPermission()) {
            return "老师在,老师上课:\n" +teacher.CanLesson();
        } else {
            return "老师不在学生代替老师上课,语文课代表正在朗读荷塘月色\n";
        }
    }
}
4.
package org.Proxy;
public class Test {
    public static void main(String[] args) {
    StudentProxy proxy = new StudentProxy();
   String message= proxy.CanLesson();
        System.out.println(message);
    }
}
对我来说写多了,好像调用来调用去没什么区别哈哈。。。




















