02_设计模式

文章目录

  • 设计模式
    • 设计模式分类
    • UML类图
    • 设计模式的原则
  • 常用设计模式
    • 创建型设计模式
      • 单例设计模式
        • 饿汉模式
        • 懒汉模式(线程不安全)
        • 懒汉模式(线程安全)- Synchronized
        • 懒汉模式(线程安全)- Double Check
        • 懒汉模式(线程安全)- 静态内部类
        • 枚举
        • 总结
      • 工厂设计模式
        • 简单工厂模式
        • 工厂方法模式
        • 抽象工厂模式
      • 建造者设计模式(Builder)
    • 结构型设计模式
      • 代理设计模式(Proxy)
        • 静态代理
        • 动态代理
    • 行为型设计模式
      • 责任链

设计模式

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式代表了最佳的实践,通常被有经验的软件开发人员所使用。

  • 借助于设计模式可以编写出非常高效的代码,可复用性以及稳健性都会比较强
  • 有助于阅读源码框架

设计模式分类

GoF(4人组)设计模式共有23种,根据用途的不同,设计模式可以分为:创建型、结构型、行为型三种。

  • 创建型模式
    • 由无到有的设计模式,是为了创建应用程序的实例
    • 例如:单例模式、工厂模式、建造者模式
  • 结构型模式
    • 在已有的实例的基础上,做了一些额外的事情
    • 例如:代理模式
  • 行为型模式
    • 多个类或者实例存在的一定的关系
    • 例如:责任链模式

在这里插入图片描述


UML类图

UML全称Unified Modeling Language,是用来进行软件设计的可视化建模工具。

见知乎链接:https://zhuanlan.zhihu.com/p/109655171

eg:

public class UserService {
    private String username;
    public String password;
    Integer age;

    public void sayUsername(String username) {
        
    }

    public String bye(String name1, Integer days) {
        return null;
    }
}

在这里插入图片描述


设计模式的原则

设计原则按照字母手写简写可以概括为SOLID原则。

  • 单一职责原则(Single Responsibility Principle
    • 让每个类的功能单一 ,尽量使得每个类只负责整个软件的功能模块中的一个
  • 开放封闭原则(Open Close Principle
    • 对新增开发,对修改封闭
    • 已有的代码直接进行修改是有很大风险的,如果有新需求,可以在已有的代码进行进一步扩展
  • 里氏替换原则(Liskov Substitution Principle
    • 凡是父类出现的地方,都可以替换为其子类;子类继承父类,尽量不要重写父类的方法
    • eg: 在这里插入图片描述
  • 迪米特法则(Least Knowledge Principle
    • 又叫作最少知道原则,指的是一个类/模块对于其他的类/模块有越少的了解越好
  • 接口分离原则(Interface Segregation Principle
    • 不要写大接口(大:接口中的方法多),否则会给实现类带来负担
  • 依赖倒置原则(Dependency Inversion Principle
    • 开发过程中,先开发接口,在开发实现类
      • 具体:实现类(抽象类的子类)
      • 抽象:接口和抽象类

常用设计模式


创建型设计模式

单例设计模式

  • 保证一个类只有一个实例对象,并提供了一个访问该实例对象的全局节点
  • eg:在整个应用程序中,如果要获得MySingleton实例,始终获得的都是同一个
  • 单例设计模式分为懒汉模式懒加载)和饿汉模式立即加载
    • 懒加载:使用的时候才获得实例
    • 立即加载:使用之前已经获得实例
  • 单例设计模式的设计原则:
      1. 构造方法私有
      1. 定义一个成员变量(私有静态的成员变量),用来接收私有构造方法构造的实例
      1. 提供一个静态方法供外部类调用这个实例
饿汉模式
  • 特点:不支持延时加载(懒加载),获取对象速度比较快;但是如果对象比较大,或者一直没有去使用,那么比较浪费内存空间。
// 单例设计模式:在整个应用程序中,如果要获得MySingleton实例,始终获得的都是同一个
// 单例手写:是面试过程中常见的问题

/** 1. 构造方法私有
 *  2. 定义一个成员变量(私有静态的成员变量),用来接收私有构造方法构造的实例
 *  3. 提供一个静态方法供外部类调用这个实例
 */
public class MySingleton {
    // new MySingleton();
    // 定义了一个实例,要初始化这个实例

    // final是不让你去额外修饰它
    private static final MySingleton instance = new MySingleton();
    private MySingleton(){}

	// 调用getInstance方法之前已经完成了实例化
    public static MySingleton getInstance(){
        return instance;
    }
}

懒汉模式(线程不安全)
/**
 * 懒加载(线程不安全):在完成instance == null这句判断之后,做了线程的切换,导致线程不安全
 */
public class MySingleton2 {
    private static MySingleton2 instance;

    private MySingleton2() {
    }

    public static MySingleton2 getInstance() {
        // 第一次使用getInstance方法的时候初始化instance
        // 如何识别是不是第一次使用instance
        // 第一次使用instance的时候是null
        if (instance == null) {
            instance = new MySingleton2();
        }
        return instance;
    }
}

懒汉模式(线程安全)- Synchronized
  • 效率低
    • 因为执行这个方法需要排队
public class MySingleton3 {
    private static MySingleton3 instance;

    private MySingleton3() {
    }

	// 使用synchornized关键字即可
    public static synchronized MySingleton3 getInstance() {
        if(instance == null){
            instance = new MySingleton3();
        }
        return instance;
    }
}
懒汉模式(线程安全)- Double Check
public class MySingleton4 {
    private static MySingleton4 instance;

    private MySingleton4() {}

    public static MySingleton4 getInstance() {
        // double check:做了两次非空的判断
        if (instance == null) {
            synchronized (MySingleton4.class) {
                // 如果这里的instance == null不判断的话,仍然有线程切换导致创建多次实例的风险
                if (instance == null) {
                    instance = new MySingleton4();
                }
            }
        }
        return instance;
    }
}

懒汉模式(线程安全)- 静态内部类
  • 静态内部类的静态代码块的加载时机,使用静态内部类的时候才执行里面的静态代码块
    • 可以把实例化的这部分代码放到静态代码块的内部中
/**
 * 懒汉模式:静态内部类的方式进行加载
 * 静态代码块中的内容只会执行一次,所以是线程安全的
 */
public class MySingleton5 {

    private MySingleton5() {
    }

    static class Inner {
        private static MySingleton5 instance;

        static {
            instance = new MySingleton5();
        }

        private static MySingleton5 getInstance() {
            return instance;
        }
    }

    // 使用该方法,才会触发静态内部类的静态代码块的初始化 -> 懒加载
    public static MySingleton5 getInstance() {
        return Inner.getInstance();
    }
}

枚举
public enum Singleton6 {

    INSTANCE;

    public static Singleton6 getInstance(){
        return INSTANCE;
    }
}
总结
  • 饿汉式:在类加载时期,便已经将instance实例对象创建了;所以这种方式是线程安全的方式,但是不支持懒加载。
  • 懒汉式:该种方式支持懒加载,但是要么不是线程安全,要么虽然是线程安全,但是需要频繁释放锁、抢夺锁,并发量较低。
  • 双重检查:既可以实现懒加载,又可以实现高并发的需求。这种方式比较完美,但是代码有一些复杂。
  • 静态内部类:使用该种方式也可以解决懒加载以及高并发的问题,代码实现起来比双重检查也是比较简洁。
  • 枚举:最简单、最完美的实现方式。

工厂设计模式

  • 工厂中一定会提供一个返回实例的方法。其中核心的好处是封装(隐藏)生产的具体细节
  • 工厂类或接口的命名方式,通常为XXXFactory
简单工厂模式
  • 只要一个工厂(函数)就可以了,那么只需要传入不同的参数,就可以返回不同的产品(实例),这种模式就叫简单工厂模式
  • 未满足开闭原则

eg:

public class SimpleFactoryExecution {
    public static void main(String[] args) {
        // 非简单工厂模式
        withoutFactory();

        // 简单工厂模式
        withFactory();
    }

    private static void withFactory() {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();

        SimpleAodiFactory simpleAodiFactory = new SimpleAodiFactory();
        Aodi aodi = simpleAodiFactory.create(s);
        aodi.run();
    }

    private static void withoutFactory() {
//        Aodi aodi = new A4();
//        aodi.run();
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        Aodi aodi = null;

        // 下面这部分属于生产的细节,要把生产细节隐藏起来
        if("A4".equals(s)){
            aodi = new A4();
        }else if("A5".equals(s)){
            aodi = new A5();
        }else{
            aodi = new Q5();
        }


        aodi.run();
    }
}


public class SimpleAodiFactory {
    // static 增不增加都是可以的
    public Aodi create(String s){
        Aodi aodi = null;
        if("A4".equals(s)){
            aodi = new A4();
        }else if("A5".equals(s)){
            aodi = new A5();
        }else{
            aodi = new Q5();
        }

        return aodi;
    }
}

工厂方法模式
  • 核心思想:创建工厂接口,增加不同的实现类之后,它里面的工厂方法就是不用的实现
  • 要求:把工厂定义为接口或抽象类,通过不同的实现类实现不同实例的生产
    • FactoryBean

eg:

// 接口类
public interface AodiFactory {
    public Aodi create();
}

public class A4Factory implements AodiFactory{
    @Override
    public Aodi create() {
        return new A4();
    }
}


public class FactoryMethodExecution {
    public static void main(String[] args) {
        AodiFactory aodiFactory = new A4Factory();
        aodiFactory.create();
    }
}
抽象工厂模式
  • 抽象工厂生产的一系列的产品
  • 上面两种工厂模式生产的产品比较单一

eg:

在这里插入图片描述


建造者设计模式(Builder)

  • 开发过程中会遇到这样一个场景,它的名字不叫XXXBuilder,但是它是采用建造者设计模式的思想来完成
  • eg:StringBuilder是建造者设计模式
  • 建造者模式也叫作生成器模式,就是分步骤创建复杂对象
  • 建造者设计模式的代码风格:
      1. 首先要创建要生产的实例(仅仅是执行了构造方法)
      1. 会提供很多设置属性值的方法
      1. 会提供返回实例的方法(方法名通常是build

eg:

@Data
public class Phone {
    private String battery;
    private String screen;
    private String os;
    private String camera;
    private String color;
    // 通过@Data提供了getter/setter方法,以及我们打印的时候用的toString方法
}


public class PhoneBuilder {
    // 当我创建PhoneBuilder实例的时候,同时会创建一个Phone的实例
    // 定义一个全局变量,意味着使用一个Builder实例的方法,其实对同一个Phone做参数设置
    private Phone phone = new Phone();

    public PhoneBuilder color(String color) {
        this.phone.setColor(color);
        return this;
    }

    public PhoneBuilder battery(String battery) {
        this.phone.setBattery(battery);
        return this;
    }

    public PhoneBuilder screen(String screen) {
        this.phone.setScreen(screen);
        return this;
    }

    public PhoneBuilder os(String os) {
        this.phone.setOs(os);
        return this;
    }

    public PhoneBuilder camera(String camera) {
        this.phone.setCamera(camera);
        return this;
    }

	// 返回实例的方法
	// 虽然这个方法叫建造,但其实在创建Builder实例的时候,要建造的实例已经实例化了
    public Phone build() {
        return this.phone;
    }
}


// 使用
public class UseBuilder {
    public static void main(String[] args) {
        PhoneBuilder builder = new PhoneBuilder();
        // 方法的连续调用
        Phone phone = builder.battery("4000mha")
                .camera("1080P")
                .color("尊贵黑")
                .screen("4K高清")
                .os("Android")
                .build();
        System.out.println("phone = " + phone);
    }
}

eg:参考StringBuilder的代码风格:

    private static void builder1() {
        StringBuilder sb = new StringBuilder();
        sb.append("hello").append(" world").append("!");
        String s = sb.toString();
        System.out.println(s);
    }

结构型设计模式

代理设计模式(Proxy)

  • 增强:满足基本的需求之外,还做了额外的事情(通用的事情)
  • 核心特点:
      1. 代理类存在和委托类一样的方法(这个一样指的是外观上)
      1. 代理类执行方法的过程中,一定会执行委托类的方法
  • 代理模式最大的优点
    • 可以不更改目标类代码的前提下,扩展目标类代码的功能。
静态代理
  • 委托类、目标类(target):UserServiceImpl
  • 代理类:UserServiceProxy
  • 要保证代理类和委托类提供的方法的外观完全一样:
      1. 实现和委托类相同的接口
      1. 继承委托类,重写委托类中的方法
  • 代理类中要调用委托类的方法
  • 静态代理最大的缺点
    • 代码较为冗余,每代理一个类,便要手动编写一个代理类
    • 代理对象和目标类对象均实现了接口,如果接口发生了修改,不仅目标类需要更改,代理类也需要同步发生修改,维护成本变高了很多

eg:

public interface UserService {
    public int insert(String name);

    public int remove(int id);
}


public class UserServiceImpl implements UserService{

    @Override
    public int insert(String name) {
        System.out.println("执行UserServiceImpl的insert方法");
        return 0;
    }

    @Override
    public int remove(int id) {
        System.out.println("执行UserServiceImpl的remove方法");
        return 0;
    }
}


// 方法1:代理类实现了和委托类相同的接口
public class UserServiceProxy implements UserService{

    private UserService userService = new UserServiceImpl();

    @Override
    public int insert(String name) {
        // 应该执行UserServiceImpl的insert方法
        int insert = userService.insert(name);
        return insert;
    }

    @Override
    public int remove(int id) {
        // 应该执行UserServiceImpl的remove方法
        int remove = userService.remove(id);
        return remove;
    }
}


// 方法2:代理类继承了委托类
public class UserServiceProxy1 extends UserServiceImpl{
    @Override
    public int insert(String name) {
        // 调用父类的方法 -> 调用了委托类的方法
        return super.insert(name);
    }

    @Override
    public int remove(int id) {
        return super.remove(id);
    }
}


public class Execution {
    public static void main(String[] args) {
        withoutProxy();

        withProxy();
    }

    private static void withProxy() {
        // 1. 获得代理对象
        UserService userService = new UserServiceProxy();

        // 2. 使用代理对象调用方法,它的外观跟委托类是一模一样的
        userService.insert("1");
    }

    private static void withoutProxy() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.insert("1");
    }
}
动态代理
  • 静态代理的代理类需要自己手动去写,动态代理的代理类不用自己手动去写
  • 分类:
    • JDK动态代理
      • 会自动生成代理类,与UserServiceProxy类似(代理类和委托类实现了相同的接口)
      • 效率比较高
      • 委托类必须实现接口
    • cglib动态代理
      • 会自动生成代理类,与UserServiceProxy1类似(代理类继承自委托类)
      • 委托类可以不实现接口

在这里插入图片描述

JDK动态代理

  • JDK动态代理,即JDK给我们提供的动态生成代理类的方式,无需引入第三方jar包,但是使用JDK动态代理有一个先决条件,那就是目标类对象必须实现了某个接口;如果目标类对象没有实现任何接口,则JDK动态代理无法使用
  • 生成的代理类中的所有的方法都会指向同一个方法:InvocationHandler的invoke方法,需要程序员来实现InvocationHandler(可以直接写实现类、也可以使用匿名内部类)的invoke方法

eg:

public interface UserService {
    public String sayHello(String name);
}


public class UserServiceImpl implements UserService{
    @Override
    public String sayHello(String name) {
        String result = "hello " + name;
        System.out.println(result);
        return result;
    }
}


@Data
public class ProxyGenerator {

    // 目标类,委托类对象
    Object target;

    // 返回代理对象
    public Object generator() {
        // JDK动态代理的代理对象生成
        // 新增一个代理的对象
        /**
         * Proxy.newProxyInstance( 1 , 2 , 3);
         * 1. 类加载器
         * 2. 委托类接口的数组
         * 3. InvocationHandler 指导代理对象中的方法做何种增强
         * 返回值:代理对象
         */
        UserService proxy = (UserService) Proxy.newProxyInstance
                (ProxyGenerator.class.getClassLoader(),
                        UserServiceImpl.class.getInterfaces(), 
                        new CustomInvocationHandle(target));
        proxy.sayHello("zs");

        return proxy;
    }

    // 匿名内部类实现
    public Object generator2() {
        UserService proxy = (UserService) Proxy.newProxyInstance
                (ProxyGenerator.class.getClassLoader(),
                        UserServiceImpl.class.getInterfaces(), 
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) 
                            throws Throwable {
                                Object invoke = method.invoke(target, args);
                                return invoke;
                            }
                        });
        return proxy;
    }
}


@AllArgsConstructor
@NoArgsConstructor
public class CustomInvocationHandle implements InvocationHandler {

    Object instance;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开启事务");
        Object invoke = method.invoke(instance, args);
        System.out.println("关闭事务");

        return invoke;
    }
}


public class JdkProxyExecution {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyGenerator proxyGenerator = new ProxyGenerator();
        proxyGenerator.setTarget(userService);
        UserService proxy = (UserService) proxyGenerator.generator();

        UserService proxy2 = (UserService) proxyGenerator.generator2();

        proxy.sayHello("zs");
    }
}



开启事务
hello zs
关闭事务
开启事务
hello zs
关闭事务

cglib动态代理

  • 生成的代理类中的所有的方法都会指向同一个方法:InvocationHandler的invoke方法,需要程序员来实现InvocationHandler(可以直接写实现类、也可以使用匿名内部类)的invoke方法
  • 主要原因在于Cglib扩展的代理类会继承自目标类所以这也要求我们的目标类不能是final修饰

eg:

@AllArgsConstructor
@NoArgsConstructor
@Data
public class CglibProxyGenerator {
    Object target;

    public Object generator(){
        // 第一个参数传委托类的class
        Object proxy = Enhancer.create(UserServiceImpl.class, new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) 
            throws Throwable {
                System.out.println("开始事务");
                Object invoke = method.invoke(target, objects);
                System.out.println("结束事务");
                return invoke;
            }
        });
        return proxy;
    }
}


public class CglibProxyExecution {
    public static void main(String[] args) {
        CglibProxyGenerator generator = new CglibProxyGenerator();
        generator.setTarget(new UserServiceImpl());

        UserService proxy = (UserService) generator.generator();
        proxy.sayHello("zs");
    }
}


开始事务
hello zs
结束事务

动态代理小结

  • 代理类中一定会包含和委托类外观一致的方法,该方法中一定会有委托类方法的调用
    • 静态代理:(instancesuper调用
    • 动态代理:method.invoke
  • JDK动态代理的委托类一定要实现接口,JDK代理对象只能使用接口来接收(猫不能接收狗)
    • 代理类实现了和委托类相同的接口
  • Cglib动态代理,接口和实现类都可以接收
    • 代理类继承委托类
  • 使用动态代理,所有的方法都会指向InvocationHandlerinvoke方法
    • 真正需要程序员开发的内容:提供InvocationHandler的实现类(定义实现类或匿名内部类),实现其中的invoke方法
  • 动态代理的优势在于:实现方法的通用的增强,把委托类中出现的相同的内容给提取出来

面试问题:代理类和委托类之间的关系是什么? 分JDK动态代理和Cglib动态代理两方面回答


行为型设计模式

责任链

责任链是一种行为设计模式,允许请求沿着链进行发送。收到请求后,每个处理者均可对请求进行处理或者将其传递给链上的下一个处理者。

eg:

将一些共性的部分放置在一个基类中,其中提供的成员变量next能够维护顺序关系,通过调用其提供的setNext方法完成顺序关系的维护,handle方法能够提供不同的

@Data
public abstract class AbstractHandler {
    AbstractHandler next;
    public void handle(){
        handleCore();
        if (next != null) {
            next.handle();
        }
    }

    protected abstract void handleCore();
}


/**
 * 1. 包含处理方法
 * 2. 执行完当前处理方法,要执行下一个处理器的处理方法
 * 3. 内部可以通过成员变量指向下一个处理器
 */
@Data
public class Handler1 extends AbstractHandler {

    //AbstractHandler next;

    /*public void handle(){
        handleCore();
        if (next != null) {
            next.handle();
        }
    }*/

    public void handleCore() {
        System.out.println("Handler1的handle");
    }
}


@Data
public class Handler2 extends AbstractHandler {
    //AbstractHandler next;
    /*public void handle(){
        handleCore();
        if (next != null) {
            next.handle();
        }
    }*/

    public void handleCore() {
        System.out.println("Handler2的handle");
    }
}


@Data
public class Handler3 extends AbstractHandler {
    //AbstractHandler next;

    /*public void handle(){
        handleCore();
        if (next != null) {
            next.handle();
        }
    }*/

    public void handleCore() {
        System.out.println("Handler3的handle");
    }
}


public class ChainExecution {
    public static void main(String[] args) {
        Handler1 handler1 = new Handler1();
        Handler2 handler2 = new Handler2();
        Handler3 handler3 = new Handler3();
        Handler4 handler4 = new Handler4();

        handler1.setNext(handler2);
        handler2.setNext(handler3);
        handler3.setNext(handler4);
        // handler1 -> handler2 -> handler3
        // handler1.handle -> handler2.handle -> handler3.handle

        handler1.handle();
    }

小结

  • 责任链模式降低了系统之间的耦合性,提升了系统的可扩展性
  • 在很多中间件、框架的内部大量地使用了该种设计模式,比如Filter的执行过程等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql不等于<>取特定值反向条件的时候字段有null值或空值读取不到数据

#小李子9479# 有如下的数据结构 &#xff0c;st_dl tinyint(4)&#xff0c;想从中读取不等于1的数据 于是写了一个sql语句 select * from tbname where st_dl<>1 返回数据为0。 修改一下 select * from tbname where IFNULL(st_dl,0)<>1 正确返回数据 IFNUL…

【深度学习与神经网络】MNIST手写数字识别1

简单的全连接层 导入相应库 import torch import numpy as np from torch import nn,optim from torch.autograd import Variable import matplotlib.pyplot as plt from torchvision import datasets, transforms from torch.utils.data import DataLoader读入数据并转为ten…

深度学习之本地部署大模型ChatGLM3-6B【大模型】【报错】

文章目录 0.前言1.模型下载2.配置环境2.1 下载项目文件2.2 配置环境 3.开始推理4.总结 0.前言 本博客将介绍ChatGLM3-6B大模型在Ubuntu上的本地部署教程 1.模型下载 由于毛毛张的服务器服务无法科学上网&#xff0c;所以模型的相关文件必须现在本地下载好&#xff0c;再上传到…

【ai技术】(1):发现一个大模型可视化项目,使用nodejs编写的,llm-viz,可以本地运行展示大模型结构。

1&#xff0c;关于项目 https://www.bilibili.com/video/BV1eF4m1c7NC/ 【ai技术】&#xff08;1&#xff09;&#xff1a;发现一个大模型可视化项目&#xff0c;使用nodejs编写的&#xff0c;llm-viz&#xff0c;可以本地运行展示大模型结构。 https://github.com/bbycroft/l…

k8s集群架构维护k8s集群以及搭建k8s集群以及k8s集群的常见问题

一、k8s架构 Kubernetes&#xff08;K8s&#xff09;是一个由Google主导开发的开源容器编排平台&#xff0c;用于自动化部署、扩展和管理容器化应用程序。它的设计目标是简化容器化应用程序在生产环境中的部署和运营。Kubernetes的架构设计复杂且高效&#xff0c;主要包括以下几…

再谈EMC Unity存储系统内存DIMM问题

以前写过一篇关于EMC Unity 存储系统的DIMM的介绍文章&#xff0c;但是最近还是遇到很多关于内存的问题&#xff0c;还有一些退货&#xff0c;所以有必要再写一篇关于EMC Unity 内存方面的问题&#xff0c;供朋友们参考。如果还有疑问&#xff0c;可以加vx&#xff1a;StorageE…

v-bind 绑定 class 与 style 基础用法

使用 v-bind 指令绑定 class 和 style 时语法相对复杂一些&#xff0c;这两者是可以互相替代的&#xff0c;均用于响应更新HTML元素的属性&#xff0c; v-bind 绑定 class 属性可以改写成绑定 style 属性&#xff0c;只是 css 属性位置变了而已。 1. 绑定 class 属性 1.1 数组…

MySQL 数据库设计范式

第一范式&#xff08;1NF&#xff09; 每一列都是不可分割的原子数据项第二范式&#xff08;2NF&#xff09; 在1NF的基础上&#xff0c;非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖) 1.函数依赖A->B&#xff0c;如果通过A属性(属性组)的值…

蓝桥杯 2023 省B 飞机降落

首先&#xff0c;这题要求的数据量比较少&#xff0c;我们可以考虑考虑暴力解法。 这题可能难在很多情况的考虑&#xff0c;比如说&#xff1a; 现在时间是10&#xff0c;有个飞机20才到&#xff0c;我们是可以干等10分钟。 #include <iostream> #include <…

编织效率之梦:Visual Studio与Windows快捷键指南

个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 前言&#xff1a; 常用快捷键整理 (用加粗标注的是我个人使用时常用的&#xff0c;其实这个…

2024-03-19 作业

作业要求&#xff1a; 1> 将白天课堂代码重新实现一遍&#xff1a; select实现的TCP并发服务器 poll实现的TCP客户端 2> 君子作业 select实现的TCP客户端 poll实现的TCP并发服务器 作业1&#xff1a; 运行代码&#xff1a; select实现的TCP并发服务器 #include<myh…

[项目设计]基于websocket实现网络对战五子棋

项目介绍 该项目旨在实现一个网页端的在线五子棋&#xff0c;将实现登陆、好友、房间、对战、观战、聊天等功能 完成该项目需要了解C、数据库MySQL、基础前端HTML/CSS/JS/Ajax、网络协议WebSocket 项目源码&#xff1a;azhe1/online_gobang - 码云 - 开源中国 (gitee.com) …

国产-高精度、可编程数字温度传感芯片-MY18E20

由工采代理的MY18E20是一款国产高精度可编程的数字模拟混合信号温度传感芯片&#xff1b;感温原理基于CMOS半导体PN节温度与带隙电压的特性关系&#xff0c;经过小信号放大、模数转换、数字校准补偿后&#xff0c;数字总线输出&#xff0c;具有精度高、一致性好、测温快、功耗低…

vue前端解析jwt

vue前端解析jwt 我们可以用在线解析看解析的结果&#xff1a;https://www.lddgo.net/encrypt/jwt-decrypt 但是如果在前端需要解析token&#xff0c;拿到其中的权限信息&#xff0c;可以这样解决。 在线的&#xff1a; 完美解决&#xff1a; 代码&#xff1a; function par…

MySQL下载和安装部署

4.1 简介 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公 司开发&#xff0c;现在已经属于 Oracle 旗下产品。MySQL 是最流行的关系型数据 库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS(Relational Database Management System&…

AI系统性学习—LangChain入门

文章目录 1、LangChain入门1.1 简介1.2 架构1.3 核心概念1.2 快速入门1.3 安装 2、LangChain Prompt Template2.1 什么是提示词模版2.1 创建一个提示词模版2.2 聊天消息提示词模版2.3 模版追加示例 3、语言模型3.1 LLM基础模型3.2 LangChain聊天模型3.3 自定义模型3.4 输出解析…

linux网络服务学习(2):vsftp

1.什么是vsftp vsftp是linux服务器上的一款使用ftp协议的软件&#xff0c;是linux上使用最广泛的ftp服务端软件 ftp协议是使用明文传输的&#xff0c;很不安全&#xff0c;一般用于局域网内的文件上传、下载 2.vsftp连接类型 ftp连接要用到2个端口&#xff1a;21、20端口。…

高效备考2024年AMC10:吃透2000-2023年1250道真题(限时免费送)

我们今天继续来随机看5道AMC10真题&#xff0c;以及详细解析&#xff0c;这些题目来自1250道完整的官方历年AMC10真题库。通过系统研究和吃透AMC10的历年真题&#xff0c;参加AMC10的竞赛就能拿到好名次。即使不参加AMC10竞赛&#xff0c;掌握了这些知识和解题思路后初中和高中…

阿里巴巴求职者必看:@SpringMVC?面试准备全攻略!

如有疑问或者更多的技术分享,欢迎关注我的微信公众号“知其然亦知其所以然”! 大家好,我是小米!今天我们来聊聊阿里巴巴面试中常见的一个热门话题:@SpringMVC!如果你对这个话题感兴趣,那就跟着我一起来揭开这个技术的神秘面纱吧! @Controller 在SpringMVC中,我们经…

Java-seata 头参数透传问题步骤详解-arthas

seata分布式事物下游不能回滚的问题; 初步分析headers中TX_XID 没有传给下游系统 通过拦截器打印上游服务日志和下游服务日志打印&#xff0c;影响上游服务不能传header 中自定意义参数的地方是启用线程的熔断策略。 feign:hystrix:enabled: false #不启用client: config:def…