常用设计模式全面总结版(JavaKotlin)

news2025/3/1 8:07:17

这篇文章主要是针对之前博客的下列文章的总结版本:

  • 《设计模式系列学习笔记》
  • 《Kotlin核心编程》笔记:设计模式
  • 【Android知识笔记】FrameWork中的设计模式

主要为了在学习了 Kotlin 之后,将 Java 的设计模式实现与 Kotin 的实现放在一起做一个对比。

一、创建型模式

单例模式(Java)

Double Check Lock + volatile 版本:

public class Singleton {
    
     /** 使用 volatile 保证对该变量的写入对另一个线程可见 */
    private volatile static Singleton mSingleton = null;
    
    private Singleton() {
   }
    
    public static Singleton getInstance() {
   
        if (mSingleton == null) {
   
            synchronized(Singleton.class) {
   
                if (mSingleton == null) {
   
                    mSingleton = new Singleton();
                }
            }
        }
        return mSingleton;
    }
}

为什么要 Double Check Lockvolatile,因为 mSingleton = new Singleton() 这一句翻译成字节码指令可能对应多条,它不是一次性完成的原子性操作,会分成三个步骤完成:

  1. 给实例分配内存,
  2. 调用构造函数初始化实例,
  3. 给变量赋值操作。

这三个步骤中后面两步可能发生指令重排序,即顺序为1-3-2,从而导致双重锁定失效,另一个线程同时访问就会拿到一个还没来得及初始化完毕的对象实例,出现问题。

volatile 在这里的主要作用是禁止指令重排序。另外 volatile 还可以保证线程可见性(但不保证原子性)。

静态内部类版本:

public class Singleton {
   

    private Singleton(){
   }
    
    /**
     * 静态内部类,内部类的实例与外部类的实例没有绑定关系,
     * 只有被调用到时才会加载,从而实现延迟加载
     */
    private static class SingletonHolder {
   
        /** 静态初始化器,由 JVM 类加载过程来保证线程安全 */
        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
   
        return SingletonHolder.instance;
    }
}

注意这个写法,只有getInstance()方法是public的,其他静态内部类及其内部静态变量都是private的。

getInstance方法第一次被调用的时候,会导致JVM 虚拟机加载 SingletonHolder 类,它的静态成员变量得到初始化;从而创建 Singleton 的实例。

补充:JVM加载类的过程:加载 -> 验证 -> 准备 -> 解析 -> 初始化

准备阶段会为SingletonHolder中的静态成员instance赋值为null,在编译时会收集静态赋值语句等组成类构造器<cinit>(),在初始化阶段会执行类构造器来初始化一个类。

触发 JVM 初始化一个类有以下几种情况:

  • 使用new关键字创建对象
  • 访问类的静态成员变量 或 对类的静态成员变量进行赋值
  • 调用类的静态方法
  • 反射调用类时,如 Class.forName()
  • 初始化子类时,会先初始化其父类(如果父类还没有进行过初始化的话)
  • 遇到启动类时,如果一个类被标记为启动类(即包含main方法),虚拟机会先初始化这个主类。
  • 实现带有默认方法的接口的类被初始化时(拥有被default关键字修饰的接口方法的类)
  • 使用 JDK7 新加入的动态语言支持时 MethodHandle

显然当调用Singleton.getInstance() -> SingletonHolder.instance 时,命中上面第 2 条,JVM 就会执行 SingletonHolder 这个类的初始化,然后调用其类构造器<cinit>(),这个时候就会初始化Singleton instance静态成员对象的实例。

在这个过程中, JVM 会保证SingletonHolder这个类的<clinit>()方法被正确的加锁同步,在多线程访问的情况下,只会有一个线程看到这个类的初始化过程,而其他线程是不可见的, 其它线程都需要等待,直到<clinit>()方法执行完毕。所以是线程安全的。

这种方式既保证了线程安全性,又保证了对象的唯一性。并且还是懒加载,只有被调用时才会初始化。

枚举单例:

public enum Singleton {
   
    INSTANCE;
    public void doSomething() {
   
        // todo
    }
}

Android源码中的单例:

  • context.getSystemService(name)context的实现类 ContextImpl 中使用静态的Map对象存储ServiceFecther对象(恶汉)
  • 线程内的单例ThreadLocal,如 ChoreographerLooper 中都使用 ThreadLocal 绑定当前线程,同一个 ThreadLocal 对象在同一个线程内返回的对象是唯一的。
  • 进程内的单例:如 ActivityManager 类的 getService() 方法返回的 IActivityManagerSingleton 单例在进程内唯一。
  • 进程间的单例ServiceManager,不同进程之间内存是隔离的,在进程间共享的单例其实是由binder驱动来保证不同的进程共用同一个对象的。对所有的进程而言,ServiceManager对应同一个binder句柄(通过 0Handle句柄引用),binder句柄处理完不同进程的请求后会转发给ServiceManager所在的进程。

Q:怎样在应用里做一个跨进程的单例?

  • 单例对象类必须实现Parcelable接口,成员变量除了基本数据类型外都必须实现Parcelable接口
  • 通过 AIDL 接口, 拿到远端进程的IBinder对象,再进行 Binder 调用远端接口的业务方法,将单例对象作为参数发布给远端进程
  • 远端进程在业务方法中将接受到单例对象保存下来使用

单例模式(kotlin)

在 kotlin 中 object 类是天生的单例模式。

object Singleton {
   
    var x: Int = 2 
    fun y() {
    }
}

这个代码翻译成 Java 字节码后发现它其实就是恶汉模式的单例

如果是一个普通类想生成单例,可以使用伴生对象 companion object 来生成:

class Singleton {
   
    companion object {
   
        var x: Int = 2
        fun y() {
    }
    }
}

如果要实现 Java 中静态内部类版本的单例模式,可以像下面这样写:

class Singleton private constructor() {
   

    private object Holder {
   
        val INSTANCE = Singleton()
    }
    
    companion object {
   
        val instance = Holder.INSTANCE
    }
}

object不能定义构造函数,但可以定义init块:

object Singleton {
   
    var x: Int = 2 
    fun y(){
    }

    init {
   
        //object不能定义构造函数,但可以定义init块
    }
}

如果要在Java中使用kotlin的单例,最好在成员属性和成员方法上分别加上 @JvmField@JvmStatic注解:

object Singleton {
   
    @JvmField var x: Int = 2  
    @JvmStatic fun y() {
    } 
}
  • @JvmField 的作用是生成java的静态成员 不会生成gettersetter方法。
  • @JvmStatic 的作用是生成java的静态方法。

object修饰的类内部方法相当于静态方法,但是伪静态,也就是内部会生成一个静态的成员对象,对类的方法调用实际上是调用的内部静态成员对象的方法,只有在方法上添加@JvmStatic才会真正的生成静态方法。

普通kotlin类(非单例)中使用使用JvmFieldJvmStatic

class Foo {
   
    //普通类中使用JvmField和JvmStatic
    companion object {
   
        @JvmField var x: Int = 2
        @JvmStatic fun y(){
     }
    }
}

注意,加的注解@JvmField@JvmStatic只针对java平台的可用,其他平台并不可行。

单例的object类仍可以继承类:

object Singleton: Runnable{
   
    override fun run() {
   

    }
}

工厂方法模式(Java)

  • 定义了一个创建对象的接口,但是由子类来决定要实例化的是哪一个类。工厂方法使类的实例化延迟到子类中。
public abstract class Product {
   
    public void method1() {
   
    }
    public abstract void method();
}
public class ConcreteProductA extends Product {
   
    @Override
    public void method2() {
   
        //ProductA的业务处理
    }
}
public class ConcreteProductB extends Product {
   
    @Override
    public void method2() {
   
        //ProductB的业务处理
    }
}
public abstract class Factory {
   
    /** 
     * 此方法必须返回一个Product类型的对象
     */
    public abstract <T extends Product> T createProduct(Class<T> c);
}
public class ConcreteFactory extends Factory {
    
    @Override
    public <T extends Product> T createProduct(Class<T> c) {
   
        //创建Product的逻辑,这里是直接调用Class的newInstance方法
        Product product = null;
        try {
   
            product = (Product) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
   
            e.printStackTrace();
        }
        return (T)product;
    }
}
public class Client {
   
    public static void main(String[] args) {
   
        Creator creator = new ConcreteCreator();
        Product productA = creator.createProduct(ConcreteProductA.class);
        Product productB = creator.createProduct(ConcreteProductB.class);
        ......
    }
}

简单工厂模式:如果实际当中只需要一个工厂类,那么就不需要抽象的工厂基类,可以把创建产品的方法改为静态方法

public class HumanFactory {
   
    @Override
    public static <T extends Human> T createHuman(Class<T> c) {
   
        Human human = null;
        try {
   
            human = (Human) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
   
            e.printStackTrace();
        }
        return (T) human;
    }
}

多个工厂类:在实际当中如果初始化创建对象的过程比较复杂,比如不同的产品可能会设置不同的参数,这个时候创建产品的方法不能共用,所以这时就需要为每一个产品类创建一个工厂类。

可生成单例模式的工厂类:既然工厂方法是用来生产对象的,那么就可以对工厂方法做简单的修改,只返回一个对象,就变成了单例模式。

public class Singleton {
   
    //不允许通过new产生对象
    private Singleton(){
   
    }
    public void doSomething() {
   
        //业务处理
    }
}

public class SingletonFactory {
   
    private static Singleton singleton;

    static {
   
        try {
   
            Class<?> clazz = Class.forName(Singleton.class.getName());
            //获取无参的构造函数
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            //设置无参的构造函数可访问
            constructor.setAccessible(true);
            //创建一个实例对象
            singleton = (Singleton) constructor.newInstance();
        } catch (Exception e) {
   
            e.printStackTrace();
        }
    }
    
    public static Singleton getSingleton() {
   
        return singleton;
    }
}

可复用缓存的工厂类:如果创建对象的过程比较复杂,或者非常耗时,可以在工厂类内部对已创建的对象进行缓存,以备下次使用。

public class ProductFactory {
   
    private static final Map<String, Product> productMap = new HashMap<>();
    public static synchronized Product createProduct(String type) {
   
        Product product = null;
        //如果缓存中有该对象实例直接使用
        if (productMap.containsKey(type)) {
   
            product = productMap.get(type);
        } else {
   
            if (type.equals("ProductA")) {
   
                product = new ConcreteProductA();
            } else {
   
                product = new ConcreteProductB();
            }
            //把创建的对象缓存起来
            productMap.put(type, product);
        }
        return product;
    }
}

Android源码中的工厂方法模式:

  • 集合类的iterator()返回的是一个新的迭代器对象,其实就是一个工厂方法。
  • ActivityonCreate()方法某种角度上可以看作是一个用于创建ContentView的工厂方法,该ContentView用于填充PhoneWindowDecorView的内容区。

抽象工厂模式(Java)

  • 为创建一组相关或依赖的对象提供一个接口,而无需指定它们的具体类。

在这里插入图片描述

/**
* 抽象工厂
*/
public interface AbstractFactory {
   
    /**创建A产品家族的产品*/
    public AbstractProductA createProductA();
    /**创建B产品家族的产品*/
    public AbstractProductB createProductB();
}
public class ConcreteFactory1 implements AbstractFactory {
   

    @Override
    public AbstractProductA createProductA() {
   
        //创建一个来自A产品家族的产品
        return new ProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
   
        //创建一个来自B产品家族的产品
        return new ProductB1();
    }
}
public class ConcreteFactory2 implements AbstractFactory {
   

    @Override
    public AbstractProductA createProductA() {
   
        //创建一个来自A产品家族的产品
        return new ProductA2();
    }

    @Override
    public AbstractProductB createProductB() {
   
        //创建一个来自B产品家族的产品
        return new ProductB2();
    }
}
/**
* 产品A家族
*/
public abstract class AbstractProductA {
   
    /**每个产品共有的方法*/
    public void shareMethod() {
   
    }
    /**每个产品不同实现的方法*/
    public abstract void doSomething();
}
public class ProductA1 extends AbstractProductA {
   
    @Override
    public void doSomething() {
   
        System.out.println("产品A1的实现方法");
    }
}
public class ProductA2 extends AbstractProductA {
   
    @Override
    public void doSomething() {
   
        System.out.println("产品A2的实现方法");
    }
}
/**
* 产品B家族
*/
public abstract class AbstractProductB {
   
    /**每个产品共有的方法*/
    public void shareMethod() {
   
    }
    /**每个产品不同实现的方法*/
    public abstract void doSomething();
}
public class ProductB1 extends AbstractProductB {
   
    @Override
    public void doSomething() {
   
        System.out.println("产品B1的实现方法");
    }
}
public class ProductB2 extends AbstractProductB {
   
    @Override
    public void doSomething() {
   
        System.out.println("产品B2的实现方法");
    }
}
public class Client {
   
    public static void main(String[] args) {
   
        //创建两个工厂
        AbstractFactory factory1 = new ConcreteFactory1();
        AbstractFactory factory2 = new ConcreteFactory2();

        //使用factory1来创建一组产品,它们来自不同的产品家族
        AbstractProductA productA1 = factory1.createProductA();
        AbstractProductB productB1 = factory1.createProductB();

        //使用factory2来创建一组产品,它们来自不同的产品家族
        AbstractProductA productA2 = factory2.createProductA();
        AbstractProductB productB2 = factory2.createProductB();

        //do something...
    }
}

抽象工厂模式其实就是每个工厂类是为生产某一类相关性很强的产品类族专门特供的版本

在这里插入图片描述

工厂方法模式(Kotlin)

用单例代替工厂类

我们已经知道 Kotlin 支持用 object 来实现 Java 中的单例模式。所以我们可以使用一个 object 单例来代替一个工厂类。

object ComputerFactory {
    // 用 object 代替 class
    fun produce(type: ComputerType): Computer {
   
        return when (type) {
   
            ComputerType.PC -> PC()
            ComputerType.Server -> Server()
        }
    }
}
fun main() {
   
    val compter = ComputerFactory.produce(ComputerType.PC)
    println(compter.cpu)
}

我们可以通过operator操作符重载invoke方法来代替produce,从而进一步简化表达:

object ComputerFactory {
      
    operator fun invoke(type: ComputerType): Computer {
   
        return when (type) {
   
            ComputerType.PC -> PC()
            ComputerType.Server -> Server()
        }
    }
}
fun main() {
   
    val compter = ComputerFactory(ComputerType.PC)
    println(compter.cpu)
}

伴生对象创建静态工厂方法

interface Computer {
   
    val cpu: String
    companion object {
   
        operator fun invoke(type: ComputerType): Computer {
   
            return when (type) {
   
                ComputerType.PC -> PC()
                ComputerType.Server -> Server()
            }
        }
    }
}

class PC(override val cpu: String = "Core") : Computer
class Server(override val cpu: String = "Xeon") : Computer

enum class ComputerType {
    PC, Server }

fun main() {
   
    val compter = Computer(ComputerType.PC)
    println(compter.cpu)
}

这样就可以在一个接口类中添加伴生对象的方式来创建静态工厂方法。

伴生对象也可以指定名字:

interface Computer {
   
    val cpu: String
    companion object Factory {
   
        operator fun invoke(type: ComputerType): Computer {
   
            return when (type) {
   
                ComputerType.PC -> PC()
                ComputerType.Server -> Server()
            }
        }
    }
}

fun main() {
   
    val compter = Computer.Factory(ComputerType.PC)
    println(compter.cpu)
}

为伴生对象添加扩展方法

主要是针对三方类库中无法直接修改源码的类。

fun Computer.Companion.fromCPU(cpu: String): ComputerType? = when(cpu) {
   
    "Core" -> ComputerType.PC
    "Xeon" -> ComputerType.Server
    else -> null
}

指定伴生对象名字的写法:

fun Computer.Factory.fromCPU(cpu: String): ComputerType? = when(cpu) {
   
    "Core" -> ComputerType.PC
    "Xeon" -> ComputerType.Server
    else -> null
}

调用:

fun main() {
   
    val type = Computer.fromCPU("Core")
    println(type)
}

抽象工厂模式(Kotlin)

Kotlin 中的可以使用内联函数 inline + reified 关键字来简化抽象工厂模式:

class Dell: Computer {
    }
class Asus: Computer {
    }
class Acer: Computer {
    }

class DellFactory: AbstractFactory() {
   
    override fun produce() = Dell()
}
class AsusFactory: AbstractFactory() {
   
    override fun produce() = Asus()
}
class AcerFactory: AbstractFactory() {
   
    override fun produce() = Acer()
}

abstract class AbstractFactory {
   

    abstract fun produce(): Computer
    
    companion object {
   
        inline operator fun <reified T : Computer> invoke(): AbstractFactory = when(T::class) {
   
            Dell::class -> DellFactory()
            Asus::class -> AsusFactory()
            Acer::class -> AcerFactory()
            else -> throw IllegalArgumentException()
        }
    }
}

fun main() {
    
    val dellFactory = AbstractFactory<Dell>()
    val dell = dellFactory.produce()
    println(dell)
}

建造者模式(Java)

  • 封装一个产品的构建过程,并允许按步骤构造。
  • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
public class Robot {
   
    private final String code;
    private final String battery;
    private final Integer height;
    private final Integer weight;

    private Robot(String code, String battery, Integer height, Integer weight) {
   
        this.code = code;
        this.battery = battery;
        this.height = height;
        this.weight = weight;
    }

    public static class Builder {
   
        private final String code;
        private String battery;
        private Integer height;
        private Integer weight;

        public Builder(String code) {
   
            this.code = code;
        }

        public Builder setBattery(String battery) {
   
            this.battery = battery;
            return this;
        }

        public Builder setHeight(int height) {
   
            this.height = height;
            return this;
        }

        public Builder setWeight(int weight) {
   
            this.weight = weight;
            return this;
        }

  

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

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

相关文章

2023 AI开发者生态报告

随着人工智能技术的飞速发展&#xff0c;全球IT市场对AI的投入持续增长&#xff0c;预计到2027年将达到4236亿美元。中国作为AI领域的重要参与者&#xff0c;其投资规模预计将占全球的9%。在这样的背景下&#xff0c;2023年的《AI开发者生态报告》为我们揭示了人工智能时代的技…

12.30_黑马数据结构与算法笔记Java

目录 320 全排列无重复 Leetcode47 321 组合 Leetcode77 分析 322 组合 Leetcode77 实现 323 组合 Leetcode77 剪枝 324 组合之和 Leetcode 39 325 组合之和 Leetcode 40 326 组合之和 Leetcode 216 327 N皇后 Leetcode51-1 328 N皇后 Leetcode51-2 329 解数独 Leetco…

喜迎元旦 | 愿新年,胜旧年,百华鞋业祝您元旦快乐,万事胜意!

一年复始岁序开&#xff0c;万象更新启新华 2023年我们聚力同行&#xff0c;相融共生&#xff0c; 凝心携手&#xff0c;奋进前行。 2024年我们挟着未知&#xff0c;带着期待&#xff0c; 继续携手砥砺前行 踏上新征程&#xff0c;向着新的奋斗目标再出发。 元旦&#xff…

C语言之整型提升

文章目录 1 有可能出现的问题2 产生以上问题的原因&#xff08;整型提升&#xff09;3 整型提升的过程4 整型提升示例5 总结 1 有可能出现的问题 代码如下 #include <stdio.h>int main () {int a -1;unsigned int b 1;if (a < b) {printf("a < b");}…

使用内网穿透轻松实现在外远程访问本地威联通QNAP NAS

文章目录 前言1. 威联通安装cpolar内网穿透2. 内网穿透2.1 创建隧道2.2 测试公网远程访问 3. 配置固定二级子域名3.1 保留二级子域名3.2 配置二级子域名 4. 使用固定二级子域名远程访问 前言 购入威联通NAS后&#xff0c;很多用户对于如何在外在公网环境下的远程访问威联通NAS…

Python:日期和时间类型学习

背景 在非开发环境经常需要做一下日期计算&#xff0c;就准备使用Python&#xff0c;顺便记下来学习的痕迹。 代码 1 1 # coding utf-82 2 3 3 from datetime import *4 4 5 5 ########################## 日期 ##########################6 6 date_now date.today()…

wsl中的Ubuntu安装远程桌面

wsl Ubuntu默认只能打开命令行&#xff0c;看不到图形化界面&#xff0c;有些操作不方便。这里介绍两种方法来远程连接到wsl里 VNC 因为win10的wsl不支持systemd&#xff0c; 所以这种方式只能是Windows11的系统&#xff0c;Window10只能用xrdp 1、禁用WSLg 在c:\users\用户…

完全适配各类中小医院专科医院和诊所的云HIS系统源码【前端:Angular+Nginx ,后台:SpringBoot】

云HIS系统采用SaaS软件应用服务模式&#xff0c;提供软件应用服务多租户机制&#xff0c;实现一中心部署多机构使用。相对传统HIS单机构应用模式&#xff0c;它可灵活应对区域医疗、医疗集团、医联体、连锁诊所、单体医院等应用场景&#xff0c;并提升区域内应用的标准化与规范…

浅谈冯诺依曼体系和操作系统

&#x1f30e;冯诺依曼体系结构 文章目录 冯诺依曼体系结构 认识冯诺依曼体系结构       硬件分类       各个硬件的简单认识         输入输出设备         中央处理器         存储器 关于内存 对冯诺依曼体系的理解 操作系统 操作系统…

为什么深度学习神经网络可以学习任何东西

下图你所看到的&#xff0c;是著名的曼德尔布罗特集&#xff0c;我们可以见证这个集合呈现出的复杂形态&#xff1a; 要理解神经网络如何学习曼德尔布罗特集&#xff0c;我们首先需要从最基础的数学概念讲起&#xff1a;什么是函数&#xff1f;函数本质上是一个将输入转化为输出…

测试用例设计方法:正交试验冲锋

1 引言 上篇讲了因果图和判定表法&#xff0c;而这两种方法在变量值很多、排列组合数量极大的场景下&#xff0c;会生成非常庞大且冗余的测试用例&#xff0c;此时我们很难对所有组合场景进行全量测试用例覆盖&#xff0c;基于此短板&#xff0c;正交试验法应运而生。 2 概念及…

CSP CCF 201409-2 画图 C++满分题解

解题思路&#xff1a; 1.使用二维数组标记每一个方块是否被涂色。 2.注意坐标代表的是点&#xff0c;不是方块&#xff0c;交界处的坐标只能算一个方块。 3.可以看成&#xff1a;每一个坐标都对应它左上角的一个小方块&#xff0c;这样可以避免重复计算方块数 #include<i…

每日一题合集1

1038 从二叉搜索树到更大和树 1038. 从二叉搜索树到更大和树 - 力扣&#xff08;LeetCode&#xff09; 二叉树的中序遍历逆向思维 给定一个二叉搜索树 root (BST)&#xff0c;请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。 提醒一下&#xff0c; …

轻量封装WebGPU渲染系统示例<55>- 顶点数据更新

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sample/VertexUpdateTest.ts 当前示例运行效果: ​​​​​​​ 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下: export class VertexUpdateTest {pr…

全新ui自动化测试框架教学——Cypress

前言 在现阶段自动化测试领域大规模普及的是selenium及appium等常规自动化测试工具&#xff0c;但在其中会有遇到很多影响因素导致测试结果不理想和不准确的情况发生。在经过Darren洋对自动化测试工具调研后&#xff0c;发现了Cypress这一款针对端到端的自动化测试工具&#xf…

从2023看2024前端发展趋势

前言 流光溯影&#xff0c;纵观2023全年&#xff0c;整个前端业界呈现出百业凋零之状&#xff0c;更不乏有“前端已死”等论调甚嚣尘上。从全局视角看IT行业&#xff0c;除了AI领域的大语言模型爆发外&#xff0c;整体都鲜有特别亮眼及突出的技术展现。故而&#xff0c;作为IT…

Linux下使用Wireshark抓包教程

在实际开发中&#xff0c;涉及网络传输的环节是非常多的。在这些过程中&#xff0c;我们经常有查看被传输的数据信息的需求&#xff0c;因此&#xff0c;抓包工具应运而生。Wireshark便是一款非常有名的抓包及分析软件&#xff0c;具有强大的协议解析能力。本文将介绍如何在Lin…

用通俗易懂的方式讲解大模型:ChatGLM3-6B 功能原理解析

上次我们介绍了 ChatGLM3-6B 的部署&#xff0c;虽然我们的大语言模型&#xff08;LLM&#xff09;部署起来了&#xff0c;新功能也试用了&#xff0c;但问题很多的小明就要问了&#xff0c;这其中的实现原理是什么呢&#xff1f;到底是怎么实现的呢&#xff1f; 那今天我们就…

第3课 使用FFmpeg获取并播放音频流

本课对应源文件下载链接&#xff1a; https://download.csdn.net/download/XiBuQiuChong/88680079 FFmpeg作为一套庞大的音视频处理开源工具&#xff0c;其源码有太多值得研究的地方。但对于大多数初学者而言&#xff0c;如何快速利用相关的API写出自己想要的东西才是迫切需要…

HTML+CSS+JAVASCRIPT实战项目——新年快乐特效

生成动态视频 <!doctype html> <html> <head><meta charset"utf-8" name"viewport" content"widthdevice-width, initial-scale1.0, maximum-scale1.0, minimum-scale1.0, user-scalableno"/><title>2024新年快乐…