详解23种设计模式——第一部分:概述+创建型模式

news2025/1/18 17:12:02

目录

1. 概述

2. 创建型模式

2.1 简单(静态)工厂模式

2.1.1 介绍

2.1.2 实现

2.2 工厂模式

2.3 抽象工厂模式

2.4 单例模式

2.4.1 饿汉模式

2.4.2 懒汉模式

2.4.3 线程安全的懒汉式

2.4.4 DCL单例 - 高性能的懒汉式

2.5 建造者模式

2.6 原型模式

2.7 创建型模式总结


1. 概述

设计模式(Design Pattern)是前辈们经过相当长的一段时间的试验和错误总结出来的,是软件开发过程中面临的通用问题的解决方案。这些解决方案使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

设计模式的优点:

  • ① 可以提高程序员的思维能力、编程能力和设计能力。
  • ② 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
  • ③ 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

有一些重要的设计原则在开篇和大家分享下,这些原则将贯通全文:

  • 针对接口编程,而不是针对实现编程。这个很重要,也是优雅的、可扩展的代码的第一步。
  • 职责单一原则。每个类都应该只有一个单一的功能,并且该功能应该由这个类完全封装起来。
  • 对修改关闭,对扩展开放。对修改关闭是说,我们辛辛苦苦加班写出来的代码,该实现的功能和该修复的 bug 都完成了,别人可不能说改就改;对扩展开放就比较好理解了,也就是说在我们写好的代码基础上,很容易实现扩展。

设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four(GoF)的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式(5种)结构型模式(7种)行为型模式(11种)

(1)创建型模式

  1. 简单(静态)工厂模式:一个工厂类根据传入的参量决定创建出那一种产品类的实例。(该模式不属于23种GOF设计模式之一
  2. 工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
  3. 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
  4. 单例模式:某个类只能有一个实例,提供一个全局的访问点。
    1. 饿汉模式
    2. 懒汉模式
  5. 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
  6. 原型模式:通过复制现有的实例来创建新的实例。

(2)结构型模式

  1. 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
  2. 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
  3. 组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
  4. 装饰模式:动态的给对象添加新的功能。
  5. 代理模式:为其他对象提供一个代理以便控制这个对象的访问。
  6. 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
  7. 亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。

(3)行为型模式

  1. 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
  2. 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
  3. 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
  4. 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
  5. 观察者模式:对象间的一对多的依赖关系。
  6. 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
  7. 中介者模式:用一个中介对象来封装一系列的对象交互。
  8. 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
  9. 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
  10. 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
  11. 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

2. 创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

2.1 简单(静态)工厂模式

2.1.1 介绍

① 一句话来说就是:一个工厂类根据传入的参量决定创建出那一种产品类的实例。因为逻辑实现简单,所以称为简单工厂模式,也因为工厂中的方法一般设置为静态,所以也称为静态工厂,它不属于23种模式

② 简单工厂模式专门定义一个工厂类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,在工厂类中,可以根据参数的不同返回不同类的实例。升级版本简单工厂模式,通过反射根据类的全路径名生成对象。

③ 简单工厂模式就是将这部分创建对象语句分离出来,由工厂类来封装实例化对象的行为,修改时只需要修改类中的操作代码,使用时调用该类不需要考虑实例化对象的行为,使得后期代码维护升级更简单方便,有利于代码的可修改性与可读性。

④ 但是如果增加新的产品的话,需要修改工厂类的判断逻辑,违背开闭原则。

2.1.2 实现

直接上代码:

public class FoodFactory {
    public static Food makeFood(String name) {
        if (name.equals("noodle")) {
            Food noodle = new LanZhouNoodle();
            noodle.addSpicy("more");
            return noodle;
        } else if (name.equals("chicken")) {
            Food chicken = new HuangMenChicken();
            chicken.addCondiment("potato");
            return chicken;
        } else {
            return null;
        }
    }
}

其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。

简单地说,简单工厂模式通常就是这样:一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象

我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

2.2 工厂模式

简单工厂模式很简单,如果它能满足我们的需要,我觉得就不要折腾了。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。

public interface FoodFactory {
    Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {
    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new ChineseFoodA();
        } else if (name.equals("B")) {
            return new ChineseFoodB();
        } else {
            return null;
        }
    }
}
public class AmericanFoodFactory implements FoodFactory {
    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new AmericanFoodA();
        } else if (name.equals("B")) {
            return new AmericanFoodB();
        } else {
            return null;
        }
    }
}

其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。

客户端调用:

public class APP {
    public static void main(String[] args) {
        // 先选择一个具体的工厂
        FoodFactory factory = new ChineseFoodFactory();
        // 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象
        Food food = factory.makeFood("A");
    }
}

虽然都是调用 makeFood("A") 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。

第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。

核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。

虽然简单,不过我也把所有的构件都画到一张图上,这样看着比较清晰:

2.3 抽象工厂模式

当涉及到产品族的时候,就需要引入抽象工厂模式了。

一个经典的例子是造一台电脑。我们先不引入抽象工厂模式,看看怎么实现。

因为电脑是由许多的构件组成的,我们将 CPU 和主板进行抽象,然后 CPU 由 CPUFactory 生产,主板由 MainBoardFactory 生产,然后,我们再将 CPU 和主板搭配起来组合在一起,如下图:

这个时候的客户端调用是这样的:

// 得到 Intel 的 CPU
CPUFactory cpuFactory = new IntelCPUFactory();
CPU cpu = intelCPUFactory.makeCPU();

// 得到 AMD 的主板
MainBoardFactory mainBoardFactory = new AmdMainBoardFactory();
MainBoard mainBoard = mainBoardFactory.make();

// 组装 CPU 和主板
Computer computer = new Computer(cpu, mainBoard);

单独看 CPU 工厂和主板工厂,它们分别是前面我们说的工厂模式。这种方式也容易扩展,因为要给电脑加硬盘的话,只需要加一个 HardDiskFactory 和相应的实现即可,不需要修改现有的工厂。

但是,这种方式有一个问题,那就是如果 Intel 家产的 CPU 和 AMD 产的主板不能兼容使用,那么这代码就容易出错,因为客户端并不知道它们不兼容,也就会错误地出现随意组合。

下面就是我们要说的产品族的概念,它代表了组成某个产品的一系列附件的集合:

当涉及到这种产品族的问题的时候,就需要抽象工厂模式来支持了。我们不再定义 CPU 工厂、主板工厂、硬盘工厂、显示屏工厂等等,我们直接定义电脑工厂,每个电脑工厂负责生产所有的设备,这样能保证肯定不存在兼容问题

这个时候,对于客户端来说,不再需要单独挑选 CPU厂商、主板厂商、硬盘厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。

public static void main(String[] args) {
    // 第一步就要选定一个“大厂”
    ComputerFactory cf = new AmdFactory();
    // 从这个大厂造 CPU
    CPU cpu = cf.makeCPU();
    // 从这个大厂造主板
    MainBoard board = cf.makeMainBoard();
    // 从这个大厂造硬盘
    HardDisk hardDisk = cf.makeHardDisk();

    // 将同一个厂子出来的 CPU、主板、硬盘组装在一起
    Computer result = new Computer(cpu, board, hardDisk);
}

当然,抽象工厂的问题也是显而易见的,比如我们要加个显示器,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则

2.4 单例模式

2.4.1 饿汉模式

饿汉模式最简单

public class Singleton {
    /**
     * static:
     * ①表示共享变量,语意符合
     * ②使得该变量能在getInstance()静态方法中使用
     * final:
     * ①final修饰的变量值不会改变即常量,语意也符合,当然不加final也是可以的
     * ②保证修饰的变量必须在类加载完成时就已经进行赋值。
     * final修饰的变量,前面一般加static
     */
    private static final Singleton singleton = new Singleton();

    /**
     * 私有化构造方法,使外部无法通过构造方法构造除singleton外的类实例
     * 从而达到单例模式控制类实例数目的目的
     */
    private Singleton(){}

    /**
     * 类实例的全局访问方法
     * 因为构造方法以及被私有化,外部不可能通过new对象来调用其中的方法
     * 加上static关键词使得外部可以通过类名直接调用该方法获取类实例
     * @return
     */
    public static Singleton getSingleton() {
        return singleton;
    }
}

说明:

① 优点:一般使用static和final修饰变量(具体作用已经在代码里描述了),只在类加载时才会初始化,以后都不会,线程绝对安全,无锁,效率高

② 缺点:类加载的时候就初始化,不管用不用,都占用空间,会消耗一定的性能(当然很小很小,几乎可以忽略不计,所以这种模式在很多场合十分常用而且十分简单)。

2.4.2 懒汉模式

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){}
    public static Singleton getSingleton() {
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

说明:

① 优点:在外部需要使用的时候才进行实例化,不使用的时候不会占用空间

② 缺点:线程不安全。看上去,这段代码没什么明显问题,但它不是线程安全的。假设当前有N个线程同时调用getInstance()方法,由于当前还没有对象生成,所以一部分同时都进入if语句new Singleton(),那么就会由多个线程创建多个多个user对象

2.4.3 线程安全的懒汉式

public class Singleton {
    private static Singleton singleton;
    private Singleton(){};
    private static synchronized Singleton getSingleton(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

说明:

① 优点:解决了懒汉式线程不安全的问题

② 缺点:线程阻塞,影响性能

2.4.4 DCL单例 - 高性能的懒汉式

public class Singleton {
 /*volatile在这里发挥的作用是:禁止指令重排序(编译器和处理器为了优化程序性能
    * 而对指令序列进行排序的一种手段。)
    * singleton = new Singleton();这句代码是非原子性操作可分为三行伪代码
    * a:memory = allocate() //分配内存,在jvm堆中分配一段区域
    * b:ctorInstanc(memory) //初始化对象,在jvm堆中的内存中实例化对象
    * c:instance = memory //赋值,设置instance指向刚分配的内存地址
    * 上面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。
    * 重排序是为了优化性能,但是不管怎么重排序,在单线程下程序的执行结果不能被改变
    * 保证最终一致性。而在多线程环境下,可能发生重排序,会影响结果。
    * ①若A线程执行到代码singleton = new Singleton()时;
    * ②同时若B线程进来执行到代码到第一层检查if (singleton == null)
    * ③当cpu切换到A线程执行代码singleton = new Singleton();时发生了指令重排序,
    * 执行了a-b,没有执行c,此时的singleton对象只有地址,没有内容。然后cpu又切换到了B线程,
    * 这时singleton == null为false(==比较的是内存地址),
    * 则代码会直接执行到了return,返回一个未初始化的对象(只有地址,没有内容)。
    * */
    private volatile static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getSingleton() {
        /*第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
        * ①当多个线程第一次进入,所有线程都进入if语句
        * ②当多个线程第二次进入,因为singleton已经不为null,因此所有线程都不会进入if语句,
        * 即不会执行锁,从而也就不会因为锁而阻塞,避免锁竞争*/
        if (singleton == null) {
            /*第一层锁,保证只有一个线程进入,
            * ①多个线程第一次进入的时候,只有一个线程会进入,其他线程处于阻塞状态
            * 当进入的线程创建完对象出去之后,其他线程又会进入创建对象,所以有了第二次if检查
            * ②多个线程第二次是进入不到这里的,因为已被第一次if检查拦截*/
            synchronized (Singleton.class) {
                /*第二层检查,防止除了进入的第一个线程的其他线程重复创建对象*/
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

说明:代码注释已详细讲解volatile在该单例模式的作用,已经双重锁的作用。

① 优点:解决了线程阻塞的问题。

② 缺点:多个线程第一次进入的时候会造成大量的线程阻塞,代码不够优雅。

静态内部类的方式

public class Singleton {
    private Singleton(){}
    private static class LayzInner{
        private static Singleton singleton = new Singleton();
    }
    public static Singleton getSingleton(){
        return LayzInner.singleton;
    }
}

说明:

① 优点:第一次类创建的时候加载,避免了内存浪费,不存在阻塞问题,线程安全,唯一性

② 缺点:序列化-漏洞:反射,会破坏内部类单例模式

2.5 建造者模式

经常碰见的 XxxBuilder 的类,通常都是建造者模式的产物。建造者模式其实有很多的变种,但是对于客户端来说,我们的使用通常都是一个模式的:

Food food = new FoodBuilder().a().b().c().build();
Food food = Food.builder().a().b().c().build();

套路就是先 new 一个 Builder,然后可以链式地调用一堆方法,最后再调用一次 build() 方法,我们需要的对象就有了。

来一个中规中矩的建造者模式:

class User {
    // 下面是“一堆”的属性
    private String name;
    private String password;
    private String nickName;
    private int age;

    // 构造方法私有化,不然客户端就会直接调用构造方法了
    private User(String name, String password, String nickName, int age) {
        this.name = name;
        this.password = password;
        this.nickName = nickName;
        this.age = age;
    }
    // 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯,
    // 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好
    public static UserBuilder builder() {
        return new UserBuilder();
    }

    public static class UserBuilder {
        // 下面是和 User 一模一样的一堆属性
        private String  name;
        private String password;
        private String nickName;
        private int age;

        private UserBuilder() {
        }

        // 链式调用设置各个属性值,返回 this,即 UserBuilder
        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder password(String password) {
            this.password = password;
            return this;
        }

        public UserBuilder nickName(String nickName) {
            this.nickName = nickName;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

        // build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。
        // 当然,可以在 “复制” 之前做点检验
        public User build() {
            if (name == null || password == null) {
                throw new RuntimeException("用户名和密码必填");
            }
            if (age <= 0 || age >= 150) {
                throw new RuntimeException("年龄不合法");
            }
            // 还可以做赋予”默认值“的功能
            if (nickName == null) {
                nickName = name;
            }
            return new User(name, password, nickName, age);
        }
    }
}

核心是:先把所有的属性都设置给 Builder,然后 build() 方法的时候,将这些属性复制给实际产生的对象

看看客户端的调用:

public class APP {
    public static void main(String[] args) {
        User d = User.builder().name("foo").password("pAss12345").age(25).build();
    }
}

说实话,建造者模式的链式写法很吸引人,但是,多写了很多“无用”的 builder 的代码,感觉这个模式没什么用。不过,当属性很多,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。我们可以在 Builder 的构造方法中强制让调用者提供必填字段,还有,在 build() 方法中校验各个参数比在 User 的构造方法中校验,代码要优雅一些。

题外话,强烈建议读者使用 lombok,用了 lombok 以后,上面的一大堆代码会变成如下这样:

@Builder
class User {
    private String  name;
    private String password;
    private String nickName;
    private int age;
}

当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其 return this 就可以了,然后就可以像下面这样调用:

User user = new User().setName("").setPassword("").setAge(20);

很多人是这么用的,但是这种写法不太优雅,不是很推荐使用。

2.6 原型模式

在某些情况下,需要创建对象的副本,但复制一个对象的成本可能很高,或者希望避免与对象的具体类耦合。例如,当创建对象的过程较为复杂,或者对象包含大量共享的状态时,使用常规的创建方法可能会导致性能下降。

原型模式的解决方案是通过复制现有对象来创建新对象,而不是从头开始构建。这允许我们以更高效的方式创建新对象,同时避免了与对象类的直接耦合。核心概念是在原型对象的基础上进行克隆,使得新对象具有与原型相同的初始状态。

原型模式很简单:有一个原型实例,基于这个原型实例产生新的实例,也就是“克隆”了

Object 类中有一个 clone() 方法,它用于生成一个新的对象,当然,如果我们要调用这个方法,java 要求我们的类必须先实现 Cloneable 接口,此接口没有定义任何方法,但是不这么做的话,在 clone() 的时候,会抛出 CloneNotSupportedException 异常。

protected native Object clone() throws CloneNotSupportedException;

注意事项: 

  • 深克隆问题:原型模式默认进行浅克隆,即复制对象本身和其引用。如果对象内部包含其他对象的引用,可能需要实现深克隆来复制整个对象结构。
  • 克隆方法的实现:某些对象可能不容易进行克隆,特别是涉及到文件、网络连接等资源的情况。

Java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化。

总之,原型模式是一种在需要创建对象副本时非常有用的设计模式,它提供了一种灵活且高效的方法来处理对象的复制需求。

2.7 创建型模式总结

创建型模式总体上比较简单,它们的作用就是为了产生实例对象,算是各种工作的第一步了,因为我们写的是面向对象的代码,所以我们第一步当然是需要创建一个对象了。

总结:

  • 工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;
  • 抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式;
  • 单例模式为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;
  • 建造者模式专门对付属性很多的那种类,为了让代码更优美;
  • 原型模式用得比较少,了解和 Object 类中的 clone() 方法相关的知识即可。

后续待更新......

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2218593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux模拟:chrony同步时间

实验材料&#xff1a; 服务器&#xff1a;linux,红帽-9.1 客户端&#xff1a;linux&#xff0c;乌班图-18.4 Server/client; 安装chrony yum install -y chrony 查看chrony的状态&#xff1a; systemctl status chronyd 服务器&#xff1a; 在/etc/chrony.conf文件里面…

理解VSCODE基于配置的设置,避免臃肿

这节课我们讲两点&#xff1a; &#xff08;一&#xff09;下载、安装、汉化及美化 VSCODE&#xff1b; &#xff08;二&#xff09;理解VSCODE中基于配置&#xff08;Profiles&#xff09;的设置&#xff08;Settings&#xff09;&#xff0c;让 VSCODE 保持清爽。 &#xff0…

无极低码课程【redis windows下服务注册密码修改】

下载Windows版本的Redis linux环境 (自行下载) 1.打开官网https://redis.io/downloads/ windows环境 1.打开github https://github.com/microsoftarchive/redis/releases 然后选择你喜欢的版本zip或msi下载 2.这里下载zip版,解压后后,打开安装目录 3.双击redis-server…

学习莫烦python---神经网络

一、卷积神经网络区别 1、“卷积” 和 “神经网络”. 卷积也就是说神经网络不再是对每个像素的输入信息做处理了,而是图片上每一小块像素区域进行处理, 这种做法加强了图片信息的连续性. 使得神经网络能看到图形, 而非一个点. 这种做法同时也加深了神经网络对图片的理解 –翻译…

快充协议有哪些,都有哪些特点

什么是PD协议 PD协议是一种充电协议&#xff0c;全称为“USB Power Delivery&#xff08;USB PD&#xff09;”&#xff0c;是由USB-IF&#xff08;USB Implementers Forum&#xff09;组织制定的一种标准协议‌。它是一种基于USB接口的快速充电技术&#xff0c;可以实现高达1…

【无人机设计与控制】基于环形拓扑的多目标粒子群优化算法(MO_Ring_PSO_SCD)求解无人机

摘要 本文提出了一种基于环形拓扑的多目标粒子群优化算法 (MO-Ring-PSO-SCD) 用于解决无人机的三维路径规划问题。该算法同时优化了无人机路径的路径成本和威胁成本&#xff0c;通过粒子群算法的多目标优化能力实现路径选择。实验结果表明&#xff0c;与传统算法相比&#xff…

RFID在半导体天车的问题解决方案

RFID在半导体天车的问题解决方案 目前苏州某科技公司的半导体天车目前现阶段存在问题&#xff1a; &#xff08;1&#xff09;传统8寸晶圆加工过程中涉及几十道工序&#xff0c;目前都是采用人工搬运&#xff0c;容易产生污染物导致晶圆损坏&#xff0c;速度也比较慢&#xf…

自动驾驶系列—自动驾驶测试前的必备流程:车辆准备平台深度解析

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

Java笔试04

采用FIFO&#xff08;先进先出&#xff09;页面置换算法&#xff0c;可能会产生 抖动现象&#xff1a;抖动现象是指频繁地进行页面置换&#xff0c;导致系统资源大部分时间用于页面置换而不是实际的计算工作。FIFO算法由于其简单的先进先出特性&#xff0c;可能会导致频繁的页面…

消费即赚,循环购物模式引领潮流

在电商界&#xff0c;一种全新的购物模式正悄然兴起——循环购物模式。听起来有些不可思议&#xff1f;消费满额还能获得高额返利&#xff0c;甚至每日领取现金&#xff1f;这背后究竟隐藏着怎样的秘密&#xff1f; 一、循环购物模式初印象 想象一下&#xff0c;您在某个电商平…

【OpenGL】创建窗口/绘制图形

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、创建窗口 1、代码流程图 2、运行结果 3、代码 二、三角形 1、顶点缓冲对象&#xff1a;Vertex Buffer Object…

Redis --- 第四讲 --- 常用数据结构 --- Hash、List

一、Hash哈希类型的基本介绍。 哈希表&#xff1a;之前学过的所有数据结构中&#xff0c;最最重要的。 1、日常开发中&#xff0c;出场频率非常高。 2、面试中&#xff0c;非常重要的考点。 Redis自身已经是键值对结构了。Redis自身的键值对就是通过哈希的方式来组织的。把…

行业认可 | 钛铂数据喜获2024爱分析 · 数据智能优秀厂商奖

近日&#xff0c;TapData 凭借在数据智能领域的卓越表现&#xff0c;成功入选2024爱分析数据智能优秀厂商榜单。 9月13日&#xff0c;2024爱分析第六届数据智能高峰论坛圆满举办。作为此次论坛的重磅环节&#xff0c;正式公布了“2024爱分析数智卓越企业奖”“2024爱分析数据智…

汽车建模用什么软件最好?汽车建模渲染建议!

在汽车建模和渲染领域&#xff0c;选择合适的软件对于实现精确的设计与高质量的视觉效果至关重要。那么不少的汽车设计师如何选择合适的建模软件与渲染方案呢&#xff0c;一起来简单看看吧&#xff01; 一、汽车建模用软件推荐 1、Alias Autodesk旗下的Alias系列软件是汽车设…

代理 IP 在 AI 爬虫中的关键应用

现如今&#xff0c;人工智能&#xff08;AI&#xff09;的发展日新月异&#xff0c;而数据作为驱动 AI 发展的关键要素&#xff0c;其重要性不言而喻。AI 爬虫作为获取大量数据的重要工具&#xff0c;在数据收集过程中发挥着至关重要的作用。而代理 IP 在 AI 爬虫中有着广泛而重…

flutter实现头像覆盖轮播滚动组件

效果如下: 支持自定义图片大小 支持设置覆盖比例 支持设置最大展示数量 支持设置缩放动画比例 支持自定义动画时长、以及动画延迟时长 支持当图片List长度小于或者登录设置的最大展示数量时禁用滚动动画。 import ../../library.dart;class CircularImageList extends Sta…

2024全网最详细CTF入门指南、CTF夺旗赛使用工具及刷题网站

2024年最新的CTF&#xff08;Capture The Flag&#xff0c;夺旗赛&#xff09;入门指南如下&#xff0c;涵盖了入门思路、常见题型及练习网站推荐&#xff0c;帮助你逐步了解并提升在CTF中的解题技巧。 如果你对网络安全入门感兴趣&#xff0c;我给大家整理好了相关资料&#…

基于SpringBoot+Vue的蜗牛兼职网的设计与实现(带文档)

基于SpringBootVue的蜗牛兼职网的设计与实现&#xff08;带文档) 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue等工具:IDEA/Ecilpse、Navicat、Maven 该系统主要分为三个角色&#xff1a;管理员、用户和企业&#xff0c;每个角色都有其独特的功能模块&#xff0c;以满…

【从零到一的笔试突破】——day1笔试巅峰(6道笔试题)ACM模式让笔试更有感觉

文章目录 数字统计&#xff08;数学模拟&#xff09;两个数组的交集&#xff08;哈希&#xff09;点击消除&#xff08;栈&#xff09;牛牛的快递&#xff08;模拟&#xff09;最小花费爬楼梯&#xff08;动态规划&#xff09;数组中两个字符串的最小距离&#xff08;滑动窗口o…

智慧社区Web平台:Spring Boot技术实现

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…