02_设计模式

news2024/11/15 17:51:37

文章目录

  • 设计模式
    • 设计模式分类
    • 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的执行过程等。

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

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

相关文章

Android 系统源码快速入门

Android源码快速入门 今天分享的内容是Android源码快速入门,主要分为以下几个步骤: * 硬件要求 * 虚拟机安装 * 开发环境搭建 * 下载编译源码 * 从一个简单的实际开发需求体验 Framework 开发硬件要求 用于 Android Framework 开发的电脑需要较强的 C…

算法沉淀——贪心算法二(leetcode真题剖析)

算法沉淀——贪心算法二 01.最长递增子序列02.递增的三元子序列03.最长连续递增序列04.买卖股票的最佳时机 01.最长递增子序列 题目链接:https://leetcode.cn/problems/longest-increasing-subsequence/ 给你一个整数数组 nums ,找到其中最长严格递增子…

【第十一章】改进神经网络学习方式-Softmax

我们将主要使用交叉熵成本来解决学习减速的问题。然而,我想简要描述另一种解决方法,基于所谓的 softmax 层神经元。在本章的剩余部分,我们实际上不会使用 softmax 层,因此如果你时间紧迫,可以跳到下一节。然而&#xf…

Vue3 依赖注入provide与inject

简介 关于provide与inject下面是vue官网上的一些介绍 通常情况下,当我们需要从父组件向子组件传递数据时,会使用props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个…

【Flask开发实战】防火墙配置文件解析(二)之shell读取内容

一、前言 上一篇文章中,介绍了防火墙配置文件包含的基本元素和格式样式,并模拟了几组有代表性的规则内容,作为基础测试数据。在拿到基础测试数据后,关于我们最终想解析成的数据是什么样式的,其实不难看出,…

GateWay路由规则

Spring Cloud GateWay 帮我们内置了很多 Predicates功能,实现了各种路由匹配规 则(通过 Header、请求参数等作为条件)匹配到对应的路由 1 时间点后匹配 server:port: 8888 spring:application:name: gateway-servicecloud:nacos:discovery:…

四.排序(冒泡/选择)

目录 11-排序介绍 常见排序算法: 12-冒泡排序介绍 代码要求: 思路: 13-冒泡排序 代码: 14-选择排序 简单写法: 好的写法: 11-排序介绍 排序:将一组“无序”的记录序列调整为“有序”的记录序列。 列表排序:将无序列表变为有序列表 输入&#…

C++第七弹---类与对象(四)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、拷贝构造函数 1.1、概念 1.2、特征 2、运算符重载 2.1、等号运算符重载 总结 1、拷贝构造函数 1.1、概念 在现实生活中,可能…

【C++】手撕AVL树

> 作者简介:დ旧言~,目前大二,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:能直接手撕AVL树。 > 毒鸡汤:放弃自…

Java安全基础 必备概念理解

Java安全基础 关键概念汇总 文章目录 Java安全基础 关键概念汇总前置知识1.构造器this以及包的使用2.继承3.重写/ 重载 / super4.多态5.区分和equals方法6.toString的使用7.Object的概念8.static,final,代码块static代码块final 9.动态代理10.类的动态加载1)类加载器含义&#…

leetCode刷题 17. 电话号码的字母组合

题目: 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:digits "23&q…

操作系统练习-操作系统的基本概念

操作系统的基本概念 ----------------------------------------------------------------------------------------------------------------------------- 1.操作系统是扩充( )功能的第一层系统软件 A.软件 B.裸机 C.机器语言 …

opengl日记8-opengl创建三角形

文章目录 环境直接上代码一点小总结参考 环境 系统:ubuntu20.04opengl版本:4.6glfw版本:3.3glad版本:4.6cmake版本:3.16.3gcc版本:10.3.0 直接上代码 CMakeLists.txt cmake_minimum_required(VERSION 2…

STM32CubeIDE基础学习-KEY按键输入实验

STM32CubeIDE基础学习-KEY按键输入实验 文章目录 STM32CubeIDE基础学习-KEY按键输入实验前言第1章 硬件介绍第2章 工程配置2.1 工程外设配置部分2.2 生成工程代码部分 第3章 代码编写第4章 实验现象总结 前言 前面学习了GPIO作为输出功能的实验,现在来学习GPIO作为…

【LIMS】微服务

目录 一、服务解决方案-Spring Cloud Alibaba1.1选用原因(基于Spring Cloud Alibaba的试用场景)1.2 核心组件使用前期规划 部署 nacos部署 mino使用JavaFreemarker模板引擎,根据XML模板文件生成Word文档使用JavaFlowable 工作流引擎前端 -vue…

C++初阶:string类的模拟自实现

目录 1. 引子2. 自实现string类功能模块3. string类功能模块的具体实现3.1 默认成员函数3.2 遍历访问相关成员函数3.3 信息插入相关成员函数3.4 信息删除3.5 信息查找3.6 非成员函数3.7 杂项成员函数 4. 补充知识 1. 引子 通过对string类的初步学习,没有对知识进行较…

记一次 .NET某施工建模软件 卡死分析

一:背景 1. 讲故事 前几天有位朋友在微信上找到我,说他的软件卡死了,分析了下也不知道是咋回事,让我帮忙看一下,很多朋友都知道,我分析dump是免费的,当然也不是所有的dump我都能搞定&#xff…

旅游行业分析及媒体邀约资源汇总

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 酒店旅游行业分析及媒体邀约资源汇总是两个相对独立但又相互关联的领域。下面将分别对这两个方面进行概述。 酒店旅游行业分析 1. 市场概况 市场规模:评估市场的总价值、增长…

【python】学习笔记04-函数

4.1 函数介绍 1. 函数是: 组织好的、可重复使用的、用来实现特定功能的代码段 2. 使用函数的好处是: • 将功能封装在函数内,可供随时随地重复利用 • 提高代码的复用性,减少重复代码,提高开发效率 4.2 函数的定义 …

视频桥接芯片#LT8912B适用于MIPIDSI转HDMI+LVDS应用方案,提供技术支持。

1. 概述 Lontium LT8912B MIPI DSI 转 LVDS 和 HDMI 桥接器采用单通道 MIPI D-PHY 接收器前端配置,每通道 4 个数据通道,每个数据通道以 1.5Gbps 的速度运行,最大输入带宽高达 6Gbps。 对于屏幕应用,该桥接器可解码 MIPI DSI 18bp…