当前位置: 首页 > news >正文

[程序设计]—代理模式

[程序设计]—代理模式👳

本文章记录学习于——52.面向切面:AOP-场景模拟_哔哩哔哩_bilibili

最近闲来无事,在学习Spring的源码: 后面慢慢更新源码系列blog,希望多多关注🙏🙏

目前已经总结的blog 系列🕳: 慈様や 前端学习导航👩🏻‍🚀🚀 、小様—Java后端开发日记

很多框架在设计和实现过程中广泛运用了多种设计模式:Spring核心IOC、AOP

  • 工厂模式: BeanFactoryApplicationContext 实现了工厂模式,负责创建和管理Bean对象

  • 单例模式: Spring 默认 将所有Bean定义为单例模式,即每个容器中只存在一个实例

  • 代理模式: AOP 面向切面编程的核心,通过动态代理:JDK代理CGLIB代理

    实现了非侵入式编程,可以在不修改原有业务代码的情况下增加额外功能

    在目标方法调用前后添加增强逻辑,如事务管理、日志记录等,

  • 适配器默认、观察者默认、策略模式… 设计模式使,

    Spring框架展现出了高度的模块化、可配置性和可扩展性,使得开发者能够高效地构建复杂的应用程序

👊 总而颜值->设计模式很重要,

设计模式其实是在软件开发过程中经过经验积累和验证总结,一套通用代码设计方案

如果熟悉了设计模式,当遇到类似的场景,我们可以快速地参考设计模式实现代码,

不仅可以加速我们的编码速度,也提升了代码的:扩展性、重用性、维护性!

个人看法:

虽然,设计模式听着高大上,其实实际开发过程中已经潜移默化的接触了很多了,

且有的设计模式已经随着时代、或还并没有完全适合的引用常见,新手建议会用为主,了解为辅

设计模式:属于程序的内功心法💖、学习是一个长期反复推敲的过程、不断优化升级🆙🆙❗ ❗ ❗

  • 注意:设计模式不属于某个特定的编程语言,而是一种编程思想,代码模式;

设计模式分类:

目前设计模式主要有:23种,大致分为三大类:

创建型模式 Creational Patterns 用于创建对象的模式,同时隐藏对象创建的逻辑,

避免代码中出现大量 new 操作和复杂的创建逻辑,目的是解耦对象的创建和使用

  • 常用的有: 单例模式、工厂模式、建造者模式
  • 不常见的有: 原型模式

结构型模式 Structural Patterns

用于处理对象组合的结构,关注类与对象的组合,目的是通过组合对象或类的方式,形成更大的结构

  • 常用的有: 适配器模式、桥接模式、组合模式、装饰器模式、代理模式
  • 不常见的有: 外观模式(门面模式)、享元模式

行为型模式Behavioral Patterns

用于定义对象相互协作以完成单个对象无法单独实现的任务,目的:是定义类和对象间的通信方式;

  • 常用的有: 观察者模式、责任链模式、策略模式、模板模式、状态模式、迭代器模式,
  • 不常见的有: 备忘录模式、命令模式、中介者模式、访问者模式、解释器模式

需求介绍:

目前有一个计算器🧮类,它有add 加sub 减 的方法,新需求: 计算前后进行日志记录输出;

声明计算器接口: 用于规范计算器类的定义;

//计算器类接口:
public interface Calculator {//计算器加函数int add(int i,int j);//计算器减函数int sub(int i,int j);
}

计算器接口实现类: 声明并实现计算器接口;

//计算器接口实现类:
public class CalculatorImpl implements Calculator {@Overridepublic int add(int i, int j) { return i+j; }@Overridepublic int sub(int i, int j) { return i-j; }
}

新需求:在加减之前进行日志记录——计算器日志接口实现类:

  • 正常情况下,大部分开发者会直接在上面实现类上改动,但为了不影响其他就创建新的实现类;
//计算器日志接口实现类: 不就是日志嘛一行代码CV的事~~
public class CalculatorLogImpl implements Calculator {@Overridepublic int add(int i, int j) {System.out.println("[日志]mul 方法开始了,参数是:"+i+","+ j);System.out.println("[日志]mul 方法结束了,结果是:"+(i + j));return i + j;}@Overridepublic int sub(int i, int j) {System.out.println("[日志]mul 方法开始了,参数是:"+i+","+ j);System.out.println("[日志]mul 方法结束了,结果是:"+(i - j));return i-j;}
}

在这里插入图片描述

轻轻松松~ 小活😀 这就结束了吗?到此为止了…

NO—NO—NO❗ ❗ ❗ 如果这个类在大一点呢?或者方法实现更复杂呢?

  • 针对日志功能实现类,我们发现有如下缺陷:对核心业务功能有干扰,附加功能分散各个业务功能中;

🆗,主角登场——代理模式:👇👇👇

JDK 代理模式

代理模式是一种结构型设计模式:

它为其他对象提供一种代理,以控制对这个对象的访问,代理对象可以在客户端和目标对象之间起到中介的作用,

在不改变目标对象接口的前提下,对目标对象的访问进行控制或增强,上述代码举例:👆👆👆

代理模式的特点:

  • 功能增强: 代理对象可以在访问实际对象之前或之后添加额外的功能
  • 间接访问: 客户端通过代理访问实际对象,代理对象负责对实际对象的控制
  • 代码解耦: 客户端不直接与实际对象交互,通过代理对象可以透明地扩展实际对象的功能

代理模式的分类:

  • 静态代理: 在编译时创建代理类,代理类与目标类实现相同的接口,
  • 动态代理: 在运行时动态生成代理类,适用于无需事先定义代理类的场景

静态代理:

静态代理:不推荐) 了解即可,实际开发中不经常使用,

代理类和被代理类在 编译时期 就确定了它们之间的代理关系,

代理类需要 实现 与被代理类 相同的接口 ,并且在代理类中持有被代理类的实例 目标对象

通过调用被代理类的方法,来完成实际的操作,在方法调用前后添加额外的逻辑; 这一点也 不高级;

//静态代理类
public class CalculatorStaticProxy implements Calculator {//被代理的目标对象private Calculator target;public CalculatorStaticProxy(Calculator target) { this.target = target; }@Override//附加功能由代理类中的代理方法来实现public int add(int i, int j) {System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);//通过目标对象来实现核心业务逻辑int addResult = target.add(i, j);System.out.println("[日志] add 方法结束了,结果是:" + addResult);return addResult;}//省略...其他实现;
}
//静态代理使用:
public class CalculatorStaticProxyMain {public static void main(String[] args) {//创建被代理类对象CalculatorImpl calculator = new CalculatorImpl();//被代理类对象 ==>构造创建 对应的 ==> 静态代理类,静态代理类中对函数进行调用扩展;CalculatorStaticProxy calStaticProxy = new CalculatorStaticProxy(calculator);//因此: 直接使用静态代理类对象即可,获得存在日志的计算机函数方法;calStaticProxy.add(1,1);}
}

在这里插入图片描述
静态代理确实实现了解耦:,但是由于代码都写死了,完全不具备任何的灵活性

  • 如果:将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码

如何: 将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现——动态代理技术

动态代理:

涉及知识: 【反射】、【类加载ClassLoad】… ,JDK 官方文档📑 Java.lang.reflect.Proxy

Java 动态代理是一种在运行时 动态地创建代理对象的机制:

  • 它允许开发者在不修改原有类代码的基础上,

  • 为目标对象创建代理,并且能够在代理对象的方法调用前后灵活地添加自定义的逻辑;

与静态代理不同: 动态代理不需要为每个目标类手动编写代理类,在JDK中,常用的实现方式是 反射

  • 反射机制: 是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力

实现原理

实现原理: 基于 Java 的java.lang.reflect包:InvocationHandler接口

开发者需要实现这个接口,并且实现invoke方法,在invoke方法中定义代理对象的方法被调用时要执行逻辑;

  • invoke方法有三个参数: proxy代理对象本身、method被调用的方法对象、args方法的参数数组;

Proxy类: 通过Proxy.newProxyInstance 方法可以 创建生成 代理对象;这个方法需要三个参数:

  • 目标对象的类加载器:目标对象.getClass().getClassLoader(),
  • 目标对象的接口数组:目标对象.getClass().getInterfaces(),
  • InvocationHandler 接口实例;

🆗,到此就获得了一个:(动态)代理对象: 使用动态代理对象,具有目标对象,一样的方法([参数]);

  • 通过代理对象方法时,实际上会调用InvocationHandler实例对象invoke方法,方法中,执行了增强逻辑;

在这里插入图片描述

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;//动态代理类
public class DynamicProxy implements InvocationHandler {//目标代理对象private Object target;public DynamicProxy(Object target) { this.target = target; }@Override//proxy代理对象本身、method被调用的方法对象、args方法的参数数组public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[日志] "+method.getName()+" 方法开始了,参数是:" + args);// 通过目标对象来实现核心业务逻辑Object addResult = method.invoke(target, args);System.out.println("[日志] "+method.getName()+" 方法结束了,结果是:" + addResult);return addResult;}
}
import org.proxyD.Calculator;
import java.lang.reflect.Proxy;
import org.proxyD.CalculatorImpl;/** 动态代理使用: */
public class DynamicProxyMain {public static void main(String[] args) {//定义目标对象、InvocationHandler接口实例CalculatorImpl calculator = new CalculatorImpl();DynamicProxy calStaticProxy = new DynamicProxy(calculator);//Proxy.newProxyInstance(目标类加载对象,目标接口数组,InvocationHandler接口实例) 动态创建生成代理对象Calculator Calculatorimpl = (Calculator) Proxy.newProxyInstance(//calculator 目标对象通过接口getClass 获得类加载对象、接口数组calculator.getClass().getClassLoader(),calculator.getClass().getInterfaces(),//InvocationHandler 接口实例calStaticProxy);//使用: 生成代理类Calculatorimpl.add(1,1);Calculatorimpl.sub(1,1);}
}

在这里插入图片描述

动态代理优势: 可以在运行时根据需要动态地创建代理对象,而不需要像静态代理那样为每个目标类提前编写代理类

代码复用性强:一个InvocationHandler实现类可以用于代理多个实现相同接口的目标对象

易于维护和扩展:由于代理逻辑集中在InvocationHandler实现类的invoke方法中,

当需要修改代理逻辑时,只需要修改这个方法即可,而不需要多个代理类进行修改;

优化—代理工厂:

考虑到代码复用与管理: 可以对上述代码进行优化一个 代理工厂,Proxy.newProxyInstance 生成代理类操作进行统一管理;

匿名内部类实现: InvocationHandler接口,重写了invoke方法;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;//动态代理工厂
public class ProxyFactory {//目标对象private Object target;public ProxyFactory(Object target) { this.target = target; }//动态代理对象函数//通过: 创建 InvocationHandler接口 匿名实现类对象,直接返回 动态代理对象;public Object getProxy(){/** newProxyInstance():创建一个代理实例 其中有三个参数: *//** 1、classLoader:加载动态生成的代理类的类加载器 */ClassLoader classLoader = target.getClass().getClassLoader();/** 2、interfaces:目标对象实现的所有接口的class对象所组成的数组 */Class<?>[] interfaces = target.getClass().getInterfaces();/** 3、通过: 创建 InvocationHandler接口 匿名实现类对象,直接返回 动态代理对象* invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法 */InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try {System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));result = method.invoke(target, args);System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);} catch (Exception e) {e.printStackTrace();System.out.println("[动态代理][日志] "+method.getName()+" 异常");} finally {System.out.println("[动态代理][日志] 方法执行完毕");}return result;}};//最后返回代理对象;return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);}
}

在这里插入图片描述

CGLIB 动态代理:

JDK动态代理是基于接口的,所以要求代理类一定是有定义接口的,

CGLIB 基于 ASM 字节码生成工具,它是通过 继承的方式生成目标类的子类来实现代理类

  • 注意 final 方法,不可继承

  • 它们之间的性能随着 JDK 版本的不同而不同:

  • JDK6 在运行次数较少的情况下,动态代理与 CGLIB 差距不明显,次数增加之后 CGLIB 更快

  • JDK7 情况发生了逆转!在运行次数较少1.000,000情况下,JDK 动态代理比 CGLIB 快了差不多30%;

    而当调用次数增加之后(50000000),JDK动态代理比 CGLIB 快了接近1倍,Jdk8 表现和 Jdk7 基本一致

CGLIB 的工作原理

继承目标类:CGLIB 通过 继承目标类 来创建代理类。在生成的代理类中,它会重写目标类的方法

方法拦截:CGLIB 利用方法拦截器(MethodInterceptor)来控制对目标类方法的访问;

  • 当代理类的方法被调用时,会先进入方法拦截器的intercept方法;进行操作;

添加 CGLIB 依赖:CGLIB 是第三方库,所以需要引入对应依赖;

<dependency><groupId>cglib</groupId><artifactId>cglib-nodep</artifactId><version>3.3.0</version>
</dependency>

创建方法拦截器:实现 CGLIB 的MethodInterceptor接口来定义方法拦截逻辑,重写:intercept方法:

  • 第一个参数o是代理对象本身

  • 第二个参数method是被调用的目标方法

  • 第三个参数objects是目标方法的参数数组

  • 第四个参数methodProxy是一个方法代理对象,用于调用目标类的原始方法

    通过methodProxy.invokeSuper(o, objects)可以调用目标类的原始方法,并获取返回结果;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//实现 CGLIB 的MethodInterceptor接口来定义方法拦截逻辑
public class CglibInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代理类在目标方法执行前的操作"+method.getName());Object result = methodProxy.invokeSuper(o, objects);System.out.println("代理类在目标方法执行后结果"+result);return result;}
}
//CGLIB 的Enhancer类来创建代理对象
import net.sf.cglib.proxy.Enhancer;
public class CglibInterceptorMain {public static void main(String[] args) {//首先创建一个Enhancer对象Enhancer enhancer = new Enhancer();//设置代理类要继承的目标类、设置方法拦截器enhancer.setSuperclass(Calculator.class);enhancer.setCallback(new CglibInterceptor());//最后通过enhancer.create()创建代理对象将其转换为目标类类型Calculator proxy = (Calculator) enhancer.create();//代理类对象调用函数;proxy.add(1,1);proxy.sub(1,1);}
}

在这里插入图片描述

🆗,代理模式大致如此,是不是so easy轻轻松松的,求点赞👍、收藏⭐ 🥰🥰🥰

相关文章:

[程序设计]—代理模式

[程序设计]—代理模式&#x1f473; 本文章记录学习于——52.面向切面&#xff1a;AOP-场景模拟_哔哩哔哩_bilibili 最近闲来无事&#xff0c;在学习Spring的源码&#xff1a; 后面慢慢更新源码系列blog&#xff0c;希望多多关注&#x1f64f;&#x1f64f; 目前已经总结的b…...

26、【OS】【Nuttx】用cmake构建工程

背景 之前wiki 14、【OS】【Nuttx】Nsh中运行第一个程序 都是用 make 构建&#xff0c;准备切换 cmake 进行构建&#xff0c;方便后续扩展开发 Nuttx cmake 适配 nuttx项目路径下输入 make distclean&#xff0c;清除之前工程配置 adminpcadminpc:~/nuttx_pdt/nuttx$ make …...

C#中序列化的选择:JSON、XML、二进制与Protobuf详解

C#中序列化的选择&#xff1a;JSON、XML、二进制与Protobuf详解 在C#开发中&#xff0c;序列化是将对象转换为可存储或传输的格式的过程&#xff0c;而反序列化则是将存储或传输的数据重新转换为对象的过程。选择合适的序列化方式对应用程序的性能、可维护性和兼容性至关重要。…...

单片机实现模式转换

[任务] 要求通过单片机实现以下功能&#xff1a; 1.单片机有三种工作模式(定义全局变量MM表示模式&#xff0c;MM1&#xff0c;2&#xff0c;3表示三种不同的模式) LED控制模式 风扇控制模式 蜂鸣器控制模式 2.可以在某一个模式下通过拓展板KEY1按键控制设备 (按…...

Shader -> SweepGradient扫描渐变着色器详解

XML文件 <com.example.myapplication.MyViewxmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_gravity"center"android:layout_height"400dp"/>自定义View代码 c…...

鼠标过滤驱动

文章目录 概述代码参考资料 概述 其编写过程大体与键盘过滤驱动相似&#xff0c;只需要切换一下附加的目标设备以及创建的设备类型等。但在该操作后依然无法捕获到Vmware创建的win7操作系统的鼠标irp信息&#xff0c;于是通过在获取鼠标驱动&#xff0c;遍历其所有的设备进而附…...

【深度学习】数据操作入门

数据操作 为了能够完成各种数据操作&#xff0c;我们需要某种方法来存储和操作数据。 通常&#xff0c;我们需要做两件重要的事&#xff1a;&#xff08;1&#xff09;获取数据&#xff1b;&#xff08;2&#xff09;将数据读入计算机后对其进行处理。 如果没有某种方法来存储…...

WIFIAP项目 5G RX二次谐波超标案例分析

一、 问题的现象及描述 采用博通WIFI方案方案的两个项目在做CE高频杂散测试时发现5G RX出现10.359 GHz的高频杂散点&#xff0c;通过更换信道&#xff0c;该杂散点跟着改变&#xff0c;最终确认该频率是5G主信号的二倍频&#xff1b;如下图&#xff1a; 二、 问题分析  由于…...

HarmonyOS(ArkUI框架介绍)

ArkUI框架介绍 ArkUI简介 基本概念 UI&#xff1a; 即用户界面。开发者可以将应用的用户界面设计为多个功能页面&#xff0c;每个页面进行单独的文件管理&#xff0c;并通过页面路由API完成页面间的调度管理如跳转、回退等操作&#xff0c;以实现应用内的功能解耦。 组件&…...

在 Ubuntu 下通过 Docker 部署 MySQL 服务器

引言 Docker 是一个开源的容器化平台&#xff0c;允许开发者将应用及其依赖打包成一个标准化的单元。MySQL 是一个广泛使用的关系型数据库管理系统&#xff0c;因其高性能、可靠性和易用性&#xff0c;成为许多应用的首选数据库。结合 Docker 和 MySQL&#xff0c;可以轻松地创…...

MCU 和 PSK

在加密和认证领域&#xff0c;MCU 和 PSK 是两个不同的概念&#xff0c;分别涉及硬件和密钥管理。下面是它们的含义和相关解释&#xff1a; 1. MCU 全称&#xff1a;Microcontroller Unit&#xff08;微控制单元&#xff09; 用途&#xff1a; MCU 是一种集成了 CPU、内存&am…...

Linux:进程概念(二.查看进程、父进程与子进程、进程状态详解)

目录 1. 查看进程 1.1 准备工作 1.2 指令&#xff1a;ps—显示当前系统中运行的进程信息 1.3 查看进程属性 1.4 通过 /proc 系统文件夹看进程 2. 父进程与子进程 2.1 介绍 2.2 getpid() \getppid() 2.3 fork()函数—通过系统调用创建进程 fork()函数疑问 3. 进程状态…...

苍穹外卖07——来单提醒和客户催单(涉及SpringTask、WebSocket协议、苍穹外卖跳过微信支付同时保证可以收到订单功能)

Spring Task介绍 应用场景&#xff1a; 信用卡每月还款提醒银行贷款每月还款提醒火车票销售系统处理未付款订单入职纪念日为用户发送通知 cron表达式 cron表达式其实就是一个字符串&#xff0c;通过cron表达式可以定义任务触发的时间。 构成规则&#xff1a;分为6或7个域&…...

C语言二级考试

你必须知道的 二级考试不是编写程序&#xff0c;或者说不只是编程的考核&#xff0c;它还会考核计算机C语言相关语言还有内涵等基础知识&#xff0c;比较全面综合&#xff08;说人话&#xff0c;要看最新考纲具备一定的基础知识&#xff09; 考试时间 120 分钟 分值 100 分&…...

IDEA Maven构建时报错:无效的目标发行版17

报错分析 报错原因&#xff1a;Maven 构建时&#xff0c;Java 版本配置不匹配 我安装的JDK版本是1.8&#xff0c;但由于种种原因&#xff0c;Maven构建时指定了 Java 17 作为目标发行版&#xff0c;从而导致错误 解决方案 首先&#xff0c;java -version&#xff0c;查看环…...

javafx 将项目打包为 Windows 的可执行文件exe

要将 JavaFX 项目打包为 .exe 文件&#xff0c;你可以使用一些工具将你的应用程序封装为 Windows 可执行文件。以下是两种常用的方法&#xff1a; 方法 1&#xff1a;使用 jpackage&#xff08;适用于 JDK 14 及更高版本&#xff09; jpackage 是 JDK 内置的工具&#xff0c;…...

Python操作Excel的库openpyxl使用入门

openpyxl 是一个用于读写 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库。以下是一些 openpyxl 的基本使用方法&#xff1a; 安装 openpyxl 首先&#xff0c;确保已经安装了 openpyxl。如果没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; pip install openp…...

数据通过canal 同步es,存在延迟问题,解决方案

当使用 Canal 同步数据到 Elasticsearch&#xff08;ES&#xff09;时&#xff0c;出现延迟问题通常源于多个因素&#xff0c;如 Canal 配置、网络延迟、ES 的负载和性能瓶颈等。以下是一些解决方案&#xff0c;帮助减少和解决延迟问题&#xff1a; 1. 优化 Canal 配置 Canal…...

了解Node.js

Node.js是一个基于V8引擎的JavaScript运行时环境&#xff0c;它允许JavaScript代码在服务器端运行&#xff0c;从而实现后端开发。Node.js的出现&#xff0c;使得前端开发人员可以利用他们已经掌握的JavaScript技能&#xff0c;扩展技能树并成为全栈开发人员。本文将深入浅出地…...

Android Studio创建新项目并引入第三方jar、aar库驱动NFC读写器读写IC卡

本示例使用设备&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1bbW3AUC&ftt&id615391857885 一、打开Android Studio,点击 File> New>New project 菜单&#xff0c;选择 要创建的项目模版&#xff0c;点击 Next 二、输入项目名称…...

Oracle Dataguard(主库为双节点集群)配置详解(4):配置备库

Oracle Dataguard&#xff08;主库为双节点集群&#xff09;配置详解&#xff08;4&#xff09;&#xff1a;配置备库 目录 Oracle Dataguard&#xff08;主库为双节点集群&#xff09;配置详解&#xff08;4&#xff09;&#xff1a;配置备库一、为备库配置静态监听1、配置 li…...

前端炫酷动画--文字(二)

目录 一、弧形边框选项卡 二、零宽字符 三、目录滚动时自动高亮 四、高亮关键字 五、文字描边 六、按钮边框的旋转动画 七、视频文字特效 八、立体文字特效让文字立起来 九、文字连续光影特效 十、重复渐变的边框 十一、磨砂玻璃效果 十二、FLIP动画 一、弧形边框…...

ceph 数据均衡

实现数据均衡的主要方法 在 Ceph 集群中,实现 OSD(对象存储守护进程)之间的数据均衡对于提升性能和资源利用率至关重要。以下是实现数据均衡的主要方法: 1. 调整 OSD 权重(Reweight) 通过调整 OSD 的权重,可以控制数据在各个 OSD 之间的分布。Ceph 提供了根据利用率或…...

代码随想录算法训练营day29

代码随想录算法训练营 —day29 文章目录 代码随想录算法训练营前言一、134. 加油站暴力解法贪心算法 二、135. 分发糖果三、860. 柠檬水找零四、406.根据身高重建队列vector版list版 总结 前言 今天是算法营的第29天&#xff0c;希望自己能够坚持下来&#xff01; 今日任务&a…...

android studio根据包名获取当前安装包信息

package com.example.myapplication2;import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log;/**** 获取版本信息*/ public class SystemHelper {/*** 获取本地软件版本号*/public stat…...

学习第六十五行

仔细观察键盘&#xff0c;会发现一个$符号&#xff0c;其实是有含义的。 在 shell 脚本中&#xff0c;美元符号 $ 有几种重要的含义&#xff1a; 变量引用&#xff1a;$ 用于引用变量的值。例如&#xff0c;如果你有一个变量 name&#xff0c;可以通过 $name 来获取它的值。 n…...

零碎的知识点(七):线性二次调节器(LQR)是什么?

线性二次调节器&#xff08;LQR&#xff09;是什么&#xff1f; 1. LQR的定义与目标2. LQR的原理性能指标 J J J最优解的计算控制律 3. LQR的性质4. 举例说明问题描述解步骤仿真结果 5. 实际应用总结 线性二次调节器&#xff08;LQR&#xff09; 是一种经典的最优控制方法&…...

Matlab一些使用技巧

代码分段 两个百分号就可以实现代码的分段&#xff0c;不同段之间会以不同的背景色显示&#xff0c;方便调试 如下&#xff1a; %% 腐蚀 stlen TimeWidth*Fs/50; %线性算子的长度&#xff0c;1/100的脉宽&#xff0c;对应0.5us&#xff0c;15个采样点 stlen 100; SE strel…...

Linux 发行版介绍与对比:Red Hat、Ubuntu、Kylin、Debian

Linux 操作系统有众多发行版&#xff08;Distros&#xff09;&#xff0c;每个发行版的设计目标、目标用户、应用场景和使用方式有所不同。常见的 Linux 发行版包括 Red Hat、Ubuntu、Kylin 和 Debian。以下是这些发行版的详细介绍与对比&#xff0c;以及它们的应用场景和使用方…...

从CentOS到龙蜥:企业级Linux迁移实践记录(龙蜥开局)

引言&#xff1a; 在我们之前的文章中&#xff0c;我们详细探讨了从CentOS迁移到龙蜥操作系统的基本过程和考虑因素。今天&#xff0c;我们将继续这个系列&#xff0c;重点关注龙蜥系统的实际应用——特别是常用软件的安装和配置。 龙蜥操作系统&#xff08;OpenAnolis&#…...

做网站的ebay网/网站专业术语中seo意思是

Description 给定一张 \(n\) 个点 \(m\) 条边的无向图&#xff0c;一开始你在点 \(1\)&#xff0c;且价值为 \(0\) 每次你可以选择一个相邻的点&#xff0c;然后走过去&#xff0c;并将价值异或上该边权 如果在点 \(n\)&#xff0c;你可以选择结束游戏 求一种方案&#xff0c;使…...

前端开发和后端开发哪个赚钱/seo的基本工作内容

射频加热系统 射频功率发生器 分为传统的功率振荡器的设计和相对较新的50欧姆功率放大器的设计。功率振荡器如图&#xff1a; 射频器和负载是功率发生器电路的一部分。工作电路的电容或者电感的变化会影响负载从谐振回路耦合得到的功率。一般都是通过改变极板的间距或者调整工…...

海南三亚做网站/小程序开发平台有哪些

Thinkphp操作当前数据库以外的数据表时&#xff0c;发现更新字段的时候返回0&#xff0c;是跨库就不没有写入权限了么&#xff1f;namespace Home\Model;use Think\Model;class CategoryModel extends Model {protected $trueTableName top_categories;protected $dbName top…...

微信网站平台建设/网站建设高端公司

1.C中类与结构的唯一区别是&#xff1a;类&#xff08;class&#xff09;定义中默认情况下的成员是private的&#xff0c;而结构&#xff08;struct&#xff09;定义中默认情况下的成员是public的。 2. ::叫作用域区分符&#xff0c;指明一个函数属于哪个类或一个数据属于哪个类…...

湖北建筑网/潍坊seo按天收费

女朋友找我斗图&#xff0c;最后斗她到自闭。 网址&#xff1a;https://www.doutula.com/ 难度不大&#xff0c;代码如下&#xff1a; # -*- coding: utf-8 -*-import random import requests from bs4 import BeautifulSoup import urllib import osBASE_URL https://www.…...

页面设计怎么设计/百度推广seo效果怎么样

在线QQ客服&#xff1a;1922638专业的SQL Server、MySQL数据库同步软件最近&#xff0c;我需要在oracle和redis之间进行数据同步&#xff0c;并发现databus作为中间件。这是linkin的开源项目(奥巴马的拳击段落显示&#xff0c;有必要在linkin上投放简历&#xff0c;这不是一个小…...