源码项目中常见设计模式及实现
原文https://mp.weixin.qq.com/s/K8yesHkTCerRhS0HfB0LeA
单例模式
单例模式是指一个类在一个进程中只有一个实例对象(但也不一定,比如Spring中的Bean的单例是指在一个容器中是单例的)
单例模式创建分为饿汉式和懒汉式,总共大概有8种写法。但是在开源项目中使用最多的主要有两种写法:
1、静态常量方式属于饿汉式,以静态变量的方式声明对象。
这种单例模式在Spring中使用的比较多
举个例子,在Spring中对于Bean的名称生成有个类AnnotationBeanNameGenerator就是单例的。
2、双端检锁dcl
除了上面一种,还有一种双重检查机制在开源项目中也使用的比较多,而且在面试中也比较喜欢问。双重检查机制方式属于懒汉式,代码如下:
public class Singleton {private volatile static Singleton INSTANCE;private Singleton() {}public static Singleton getInstance() {if (INSTANCE == null) {synchronized (Singleton.class) {if (INSTANCE == null) {INSTANCE = new Singleton();}}}return INSTANCE;}}
之所以这种方式叫双重检查机制,主要是在创建对象的时候进行了两次INSTANCE == null的判断。
疑问讲解
这里解释一下双重检查机制的三个疑问:
- 外层判断null的作用
- 内层判断null的作用
- 变量使用volatile关键字修饰的作用
外层判断null的作用:其实就是为了减少进入同步代码块的次数,提高效率。你想一下,其实去了外层的判断其实是可以的,但是每次获取对象都需要进入同步代码块,实在是没有必要。
内层判断null的作用:防止多次创建对象。假设AB同时走到同步代码块,A先抢到锁,进入代码,创建了对象,释放锁,此时B进入代码块,如果没有判断null,那么就会直接再次创建对象,那么就不是单例的了,所以需要进行判断null,防止重复创建单例对象。
volatile关键字的作用:防止重排序。因为创建对象的过程不是原子,大概会分为三个步骤
第一步:分配内存空间给Singleton这个对象
第二步:初始化对象
第三步:将INSTANCE变量指向Singleton这个对象内存地址
假设没有使用volatile关键字发生了重排序,第二步和第三步执行过程被调换了,也就是先将INSTANCE变量指向Singleton这个对象内存地址,再初始化对象。这样在发生并发的情况下,另一个线程经过第一个if非空判断时,发现已经为不为空,就直接返回了这个对象,但是此时这个对象还未初始化,内部的属性可能都是空值,一旦被使用的话,就很有可能出现空指针这些问题。
双重检查机制在dubbo中的应用
在dubbo的spi机制中获取对象的时候有这样一段代码:
其实在sentinel,nacos中也是非常常见的!!
建造者模式
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。
通俗的说:建造者模式就是将一个复杂对象的构建过程优雅的拆分,按需构建目标对象
为什么有这种需求? 当一个对象参数属性非常多时,很多属性时不必要的,那么为了简单优雅创建对象,一般会采用建造者模式
上面的意思看起来很绕,其实在实际开发中,其实建造者模式使用的还是比较多的,比如有时在创建一个pojo对象时,就可以使用建造者模式来创建:
PersonDTO personDTO = PersonDTO.builder().name("三友的java日记").age(18).sex(1).phone("188****9527").build();
上面这段代码就是通过建造者模式构建了一个PersonDTO对象,所以建造者模式又被称为Budiler模式。
这种模式在创建对象的时候看起来比较优雅,当构造参数比较多的时候,适合使用建造者模式。
接下来就来看看建造者模式在开源项目中是如何运用的
1、在Spring中的运用
我们都知道,Spring在创建Bean之前,会将每个Bean的声明封装成对应的一个BeanDefinition,而BeanDefinition会封装很多属性,所以Spring为了更加优雅地创建BeanDefinition,就提供了BeanDefinitionBuilder这个建造者类。
工厂模式
工厂模式在开源项目中也使用的非常多,具体的实现大概可以细分为三种:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
工厂模式跟建造者模式的主要区别: 建造者模式是将复杂对象通过简单优雅的方式显示的进行按需创建对象,工厂模式是将复杂对象的创建过程封装,调用者无需了解具体创建流程,直接调用
1、简单工厂模式
简单工厂模式,就跟名字一样,的确很简单。比如说,现在有个动物接口Animal,具体的实现有猫Cat、狗Dog等等,而每个具体的动物对象创建过程很复杂,有各种各样地步骤,此时就可以使用简单工厂来封装对象的创建过程,调用者不需要关心对象是如何具体创建的。
通俗的讲:简单工厂模式,将复杂对象创建的过程封装,调用者可以方便的通过工程生产对象,不需要了几次创建的过程
public class SimpleAnimalFactory {public Animal createAnimal(String animalType) {if ("cat".equals(animalType)) {Cat cat = new Cat();//一系列复杂操作return cat;} else if ("dog".equals(animalType)) {Dog dog = new Dog();//一系列复杂操作return dog;} else {throw new RuntimeException("animalType=" + animalType + "无法创建对应对象");}}}
当需要使用这些对象,调用者就可以直接通过简单工厂创建就行。
SimpleAnimalFactory animalFactory = new SimpleAnimalFactory();
Animal cat = animalFactory.createAnimal("cat");
需要注意的是,一般来说如果每个动物对象的创建只需要简单地new一下就行了,那么其实就无需使用工厂模式,工厂模式适合对象创建过程复杂的场景。
2、工厂方法模式
上面说的简单工厂模式看起来没啥问题,但是还是违反了七大设计原则的OCP原则,也就是开闭原则。所谓的开闭原则就是对修改关闭,对扩展开放。
什么叫对修改关闭?就是尽可能不修改的意思。就拿上面的例子来说,如果现在新增了一种动物兔子,那么createAnimal方法就得修改,增加一种类型的判断,那么就此时就出现了修改代码的行为,也就违反了对修改关闭的原则。
所以解决简单工厂模式违反开闭原则的问题,就可以使用工厂方法模式来解决。
/*** 工厂接口*/
public interface AnimalFactory {Animal createAnimal();
}/*** 小猫实现*/
public class CatFactory implements AnimalFactory {@Overridepublic Animal createAnimal() {Cat cat = new Cat();//一系列复杂操作return cat;}
}/*** 小狗实现*/
public class DogFactory implements AnimalFactory {@Overridepublic Animal createAnimal() {Dog dog = new Dog();//一系列复杂操作return dog;}
}
这种方式就是工厂方法模式。他将动物工厂提取成一个接口AnimalFactory,具体每个动物都各自实现这个接口,每种动物都有各自的创建工厂,如果调用者需要创建动物,就可以通过各自的工厂来实现。
AnimalFactory animalFactory = new CatFactory();
Animal cat = animalFactory.createAnimal();
此时假设需要新增一个动物兔子,那么只需要实现AnimalFactory接口就行,对于原来的猫和狗的实现,其实代码是不需要修改的,遵守了对修改关闭的原则,同时由于是对扩展开放,实现接口就是扩展的意思,那么也就符合扩展开放的原则。
3、抽象工厂模式
工厂方法模式其实是创建一个产品的工厂,比如上面的例子中,AnimalFactory其实只创建动物这一个产品。而抽象工厂模式特点就是创建一系列产品,比如说,不同的动物吃的东西是不一样的,那么就可以加入食物这个产品,通过抽象工厂模式来实现。
4、工厂模式在Mybatis的运用
在Mybatis中,当需要调用Mapper接口执行sql的时候,需要先获取到SqlSession,通过SqlSession再获取到Mapper接口的动态代理对象,而SqlSession的构造过程比较复杂,所以就提供了SqlSessionFactory工厂类来封装SqlSession的创建过程。
对于使用者来说,只需要通过SqlSessionFactory来获取到SqlSession,而无需关心SqlSession是如何创建的。
5、工厂模式在Spring中的运用
BeanFactory就是Bean生成的工厂。一个Spring Bean在生成过程中会经历复杂的一个生命周期,而这些生命周期对于使用者来说是无需关心的,所以就可以将Bean创建过程的逻辑给封装起来,提取出一个Bean的工厂。
策略模式
策略模式也比较常见,就比如说在Spring源码中就有很多地方都使用到了策略模式。
通俗来讲:策略模式就是减少代码中大量if-else的过程
假设现在有一个需求,需要将消息推送到不同的平台。最简单的做法其实就是使用if else来做判断就行了。
public void notifyMessage(User user, String content, int notifyType) {if (notifyType == 0) {//调用短信通知的api发送短信} else if (notifyType == 1) {//调用app通知的api发送消息}
}
根据不同的平台类型进行判断,调用对应的api发送消息。
虽然这样能实现功能,但是跟上面的提到的简单工厂的问题是一样的,同样违反了开闭原则。当需要增加一种平台类型,比如邮件通知,那么就得修改notifyMessage的方法,再次进行else if的判断,然后调用发送邮件的邮件发送消息。
此时就可以使用策略模式来优化了。
1、策略模式实现
定义一个接口
public interface MessageNotifier {/*** 是否支持改类型的通知的方式** @param notifyType 0:短信 1:app* @return*/boolean support(int notifyType);/*** 通知** @param user* @param content*/void notify(User user, String content);}
邮件实现
@Component
public class SMSMessageNotifier implements MessageNotifier {@Overridepublic boolean support(int notifyType) {return notifyType == 0;}@Overridepublic void notify(User user, String content) {//调用短信通知的api发送短信}
}
app通知实现
public class AppMessageNotifier implements MessageNotifier {@Overridepublic boolean support(int notifyType) {return notifyType == 1;}@Overridepublic void notify(User user, String content) {//调用通知app通知的api}
}
最后notifyMessage的实现只需要要循环调用所有的MessageNotifier的support方法,一旦support方法返回true,说明当前MessageNotifier支持该类的消息发送,最后再调用notify发送消息就可以了。
@Resource
private List<MessageNotifier> messageNotifiers;public void notifyMessage(User user, String content, int notifyType) {for (MessageNotifier messageNotifier : messageNotifiers) {if (messageNotifier.support(notifyType)) {messageNotifier.notify(user, content);}}
}
那么如果现在需要支持通过邮件通知,只需要实现MessageNotifier接口,注入到Spring容器就行,其余的代码根本不需要有任何变动。
到这其实可以更好的理解策略模式了。就拿上面举的例子来说,短信通知,app通知等其实都是发送消息一种策略,而策略模式就是需要将这些策略进行封装,抽取共性,使这些策略之间相互替换。
2、策略模式在消息推送组件中使用
详情看message-push
3、策略模式在SpringMVC中的运用
比如说,我们经常在写接口的时候,会使用到了@PathVariable、@RequestParam、@RequestBody等注解,一旦我们使用了注解,SpringMVC会处理注解,从请求中获取到参数,然后再调用接口传递过来,而这个过程,就使用到了策略模式。
对于这类参数的解析,SpringMVC提供了一个策略接口HandlerMethodArgumentResolver
这个接口的定义就跟我们上面定义的差不多,不同的参数处理只需要实现这个解决就行,比如上面提到的几个注解,都有对应的实现。
比如处理@RequestParam注解的RequestParamMethodArgumentResolver的实现。
SpringMVC对于返回值的处理也是基于策略模式来实现的。
HandlerMethodReturnValueHandler接口定义跟上面都是同一种套路。
比如说,常见的对于@ResponseBody注解处理的实现RequestResponseBodyMethodProcessor。
模板方法模式
模板方法模式是指,在父类中定义一个操作中的框架,而操作步骤的具体实现交由子类做。其核心思想就是,对于功能实现的顺序步骤是一定的,但是具体每一步如何实现交由子类决定。
通俗来讲: 模板方法模式就是将一个功能的实现步骤顺序进行规定,但功能节点具体如何实现交由子类定义
比如说,对于旅游来说,一般有以下几个步骤:
- 做攻略,选择目的地
- 收拾行李
- 乘坐交通工具去目的地
- 玩耍、拍照
- 乘坐交通工具去返回
但是对于去哪,收拾什么东西都,乘坐什么交通工具,都是由具体某个旅行来决定。
那么对于旅游这个过程使用模板方法模式翻译成代码如下:
public abstract class Travel {public void travel() {//做攻略makePlan();//收拾行李packUp();//去目的地toDestination();//玩耍、拍照play();//乘坐交通工具去返回backHome();}protected abstract void makePlan();protected abstract void packUp();protected abstract void toDestination();protected abstract void play();protected abstract void backHome();}
对于某次旅行来说,只需要重写每个步骤该做的事就行,比如说这次可以选择去杭州西湖,下次可以去长城,但是对于旅行过程来说是不变了,对于调用者来说,只需要调用暴露的travel方法就行。
可能这说的还是比较抽象,我再举两个模板方法模式在源码中实现的例子。
1、模板方法模式在HashMap中的使用
HashMap我们都很熟悉,可以通过put方法存元素,并且在元素添加成功之后,会调用一下afterNodeInsertion方法。
而afterNodeInsertion其实是在HashMap中是空实现,什么事都没干。
这其实就是模板方法模式。HashMap定义了一个流程,那就是当元素成功添加之后会调用afterNodeInsertion,子类如果需要在元素添加之后做什么事,那么重写afterNodeInsertion就行。
正巧,JDK中的LinkedHashMap重写了这个方法。
2、模板方法模式在Spring中的运用
我们都知道,在Spring中,ApplicationContext在使用之前需要调用一下refresh方法,而refresh方法就定义了整个容器刷新的执行流程代码。
在整个刷新过程有一个onRefresh方法
而onRefresh方法默认是没有做任何事,并且在注释上有清楚两个单词Template method,翻译过来就是模板方法的意思,所以onRefresh就是一个模板方法,
并且方法内部的注释也表明了,这个方法是为了子类提供的。
在Web环境下,子类会重写这个方法,然后创建一个Web服务器。
3、模板方法模式在Mybatis中的使用
在Mybatis中,是使用Executor执行Sql的。
而Mybatis一级缓存就在Executor的抽象实现中BaseExecutor实现的。如图所示,红圈就是一级缓存
责任链模式
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,由该链上的某一个对象或者某几个对象决定处理此请求,每个对象在整个处理过程中值扮演一个小小的角色。
通俗来讲:责任链模式主要是将处理流程构成一条顺序执行链,链上每个节点,有各自不同的责任,对请求进行处理
举个例子,现在有个请假的审批流程,根据请假的人的级别审批到的领导不同,比如有有组长、主管、HR、分管经理等等。
先需要定义一个处理抽象类,抽象类有个下一个处理对象的引用,提供了抽象处理方法,还有一个对下一个处理对象的调用方法。
责任链实现
定义一个抽象类,保存下一个执行节点
public abstract class ApprovalHandler {/*** 责任链中的下一个处理对象*/protected ApprovalHandler next;/*** 设置下一个处理对象** @param approvalHandler*/public void nextHandler(ApprovalHandler approvalHandler) {this.next = approvalHandler;}/*** 处理** @param approvalContext*/public abstract void approval(ApprovalContext approvalContext);/*** 调用下一个处理对象** @param approvalContext*/protected void invokeNext(ApprovalContext approvalContext) {if (next != null) {next.approval(approvalContext);}}}
几种审批人的实现
//组长审批实现
public class GroupLeaderApprovalHandler extends ApprovalHandler {@Overridepublic void approval(ApprovalContext approvalContext) {System.out.println("组长审批");//调用下一个处理对象进行处理invokeNext(approvalContext);}
}//主管审批实现
public class DirectorApprovalHandler extends ApprovalHandler {@Overridepublic void approval(ApprovalContext approvalContext) {System.out.println("主管审批");//调用下一个处理对象进行处理invokeNext(approvalContext);}
}//hr审批实现
public class HrApprovalHandler extends ApprovalHandler {@Overridepublic void approval(ApprovalContext approvalContext) {System.out.println("hr审批");//调用下一个处理对象进行处理invokeNext(approvalContext);}
}
有了这几个实现之后,接下来就需要对对象进行组装,组成一个链条,比如在Spring中就可以这么玩。
@Component
public class ApprovalHandlerChain {@Autowiredprivate GroupLeaderApprovalHandler groupLeaderApprovalHandler;@Autowiredprivate DirectorApprovalHandler directorApprovalHandler;@Autowiredprivate HrApprovalHandler hrApprovalHandler;public ApprovalHandler getChain() {//组长处理完下一个处理对象是主管groupLeaderApprovalHandler.nextHandler(directorApprovalHandler);//主管处理完下一个处理对象是hrdirectorApprovalHandler.nextHandler(hrApprovalHandler);//返回组长,这样就从组长开始审批,一条链就完成了return groupLeaderApprovalHandler;}}
之后对于调用方而言,只需要获取到链条,开始处理就行。
一旦后面出现需要增加或者减少审批人,只需要调整链条中的节点就行,对于调用者来说是无感知的。
1、在SpringMVC中的使用
在SpringMVC中,可以通过使用HandlerInterceptor对每个请求进行拦截。实际上就是责任链拦截器
2、在sentinel中使用
Sentinel是阿里开源的一个流量治理组件,而Sentinel核心逻辑的执行其实就是一条责任链。
在Sentinel中,有个核心抽象类AbstractLinkedProcessorSlot
这个组件内部也维护了下一个节点对象,这个类扮演的角色跟例子中的ApprovalHandler类是一样的,写法也比较相似。这个组件有很多实现
比如有比较核心的几个实现
- DegradeSlot:熔断降级的实现
- FlowSlot:流量控制的实现
- StatisticSlot:统计的实现,比如统计请求成功的次数、异常次数,为限流提供数据来源
- SystemSlot:根据系统规则来进行流量控制
代理模式
代理模式分为动态代理和静态代理
代理模式也是开源项目中很常见的使用的一种设计模式,这种模式可以在不改变原有代码的情况下增加功能。
举个例子,比如现在有个PersonService接口和它的实现类PersonServiceImpl
//接口
public interface PersonService {void savePerson(PersonDTO person);}//实现
public class PersonServiceImpl implements PersonService{@Overridepublic void savePerson(PersonDTO person) {//保存人员信息}
}
这个类刚开始运行的好好的,但是突然之前不知道咋回事了,有报错,需要追寻入参,所以此时就可以这么写。
public class PersonServiceImpl implements PersonService {@Overridepublic void savePerson(PersonDTO person) {log.info("savePerson接口入参:{}", JSON.toJSONString(person));//保存人员信息}
}
这么写,就修改了代码,万一以后不需要打印日志了呢,岂不是又要修改代码,不符和之前说的开闭原则,那么怎么写呢?可以这么玩。
public class PersonServiceProxy implements PersonService {private final PersonService personService = new PersonServiceImpl();@Overridepublic void savePerson(PersonDTO person) {log.info("savePerson接口入参:{}", JSON.toJSONString(person));personService.savePerson(person);}
}
1、代理模式在Mybtais中的使用
其实不光是一级缓存是通过Executor实现的,二级缓存其实也是,只不过不在BaseExecutor里面实现,而是在CachingExecutor中实现的。
适配器模式
适配器模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作,将一个类的接口转换成客户希望的另一个接口。
通俗来讲:将两个乃至多个不兼容的接口,技术,进行适配兼容
1、适配器模式在日志中的使用
在日常开发中,日志是必不可少的,可以帮助我们快速快速定位问题,但是日志框架比较多,比如Slf4j、Log4j等等,一般同一系统都使用一种日志框架。
但是像Mybatis这种框架来说,它本身在运行的过程中也需要产生日志,但是Mybatis框架在设计的时候,无法知道项目中具体使用的是什么日志框架,所以只能适配各种日志框架,项目中使用什么框架,Mybatis就使用什么框架。
不同日志框架只要实现该接口即可
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
这是什么意思呢,举个例子来说,假设发生了火灾,可能需要打119、救人,那么就可以基于观察者模式来实现,打119、救人的操作只需要观察火灾的发生,一旦发生,就触发相应的逻辑。
观察者的核心优点就是观察者和被观察者是解耦合的。就拿上面的例子来说,火灾事件(被观察者)根本不关系有几个监听器(观察者),当以后需要有变动,只需要扩展监听器就行,对于事件的发布者和其它监听器是无需做任何改变的。
观察者模式实现起来比较复杂,这里我举一下Spring事件的例子来说明一下
在spring中使用观察中模式
Spring事件的实现比较简单,其实就是当Bean在生成完成之后,会将所有的ApplicationListener接口实现(监听器)添加到ApplicationEventMulticaster中。
ApplicationEventMulticaster可以理解为一个调度中心的作用,可以将事件通知给监听器,触发监听器的执行。
retrieverCache中存储了事件类型和对应监听器的缓存。当发布事件的时候,会通过事件的类型找到对应的监听器,然后循环调用监听器。
发布订阅模式
通俗来讲:发布订阅模式是将发布方(生产方),订阅方(消费方)进行分离接耦,当有数据时,通知消费方消费,或主动去消费即可
生产中常见于,webflux(reactor-stream模型),nacos(服务注册和发现),队列
总结
常见设计模式:
- 策略模式
- 责任链模式
- 代理模式
- 建造者模式
- 工厂模式
- 适配器模式
- 模版方法模式
- 观察者模式
相关文章:
源码项目中常见设计模式及实现
原文https://mp.weixin.qq.com/s/K8yesHkTCerRhS0HfB0LeA 单例模式 单例模式是指一个类在一个进程中只有一个实例对象(但也不一定,比如Spring中的Bean的单例是指在一个容器中是单例的) 单例模式创建分为饿汉式和懒汉式,总共大概…...
KDNM5000-10A-2剩余电流保护器测试仪
一、产品概述 KDNM5000-10A-2型剩余电流保护器测试仪(以下简称测试仪),是本公司改进产品,是符合国家标准《剩余电流动作保护器》(GB6829—95)中第8.3条和GB16917.1—1997中第9.9条验证AC型交流脱扣器动作特性要求的专用测试仪器。…...
C++实现线程池
C实现线程池一、前言二、线程池的接口设计2.1、类封装2.2、线程池的初始化2.3、线程池的启动2.4、线程池的停止2.5、线程的执行函数run()2.6、任务的运行函数2.7、等待所有线程结束三、测试线程池四、源码地址总结一、前言 C实现的线程池,可能涉及以下知识点&#…...
2023最新Java面试手册(性能优化+微服务架构+并发编程+开源框架)
Java面试手册 一、性能优化面试专栏 1.1、 tomcat性能优化整理 1.2、JVM性能优化整理 1.3、Mysql性能优化整理 二、微服务架构面试专栏 2.1、SpringCloud面试整理 2.2、SpringBoot面试整理 2.3、Dubbo面试整理 三、并发编程高级面试专栏 四、开源框架面试题专栏 4.1、Sprin…...
对灵敏度分析技术进行建模(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
完整爬虫学习笔记(第一章)
文章目录前言:fu:. 爬虫概述:hotdog:原理解剖:one: 服务器渲染:two: 前端JS渲染:fire: 第一个爬虫程序案例总结前言 最近正在学习Python网络爬虫的相关知识,鉴于本人Python水平有限 , 对Python并无太深的理解,所以此文章的主要目的在于抛砖引玉…...
会计师项目管理软件是什么,哪些必不可少的功能
欢迎阅读现代金融专业人士的会计师项目管理指南。在本文中,我们将深入探讨在基于项目的会计的各个方面使用项目管理方法的好处。我们还将教您面临哪些挑战以及如何为您的团队选择最佳工具。 为什么会计师的项目管理很重要? 在会计方面,目标始…...
第 8 章 优化
目录 8.1 优化概述 8.2 优化 SQL 语句 8.3 优化和指标 8.4 优化数据库结构 8.5 优化 InnoDB 表 8.6 优化 MyISAM 表 8.7 内存表的优化 8.8 了解查询执行计划 8.9 控制查询优化器 8.10 缓冲和缓存 8.11 优化锁定操作 8.12 优化 MySQL 服务器 8.13 衡量性能ÿ…...
剑指offer -- java题解
剑指offer -- java题解刷题地址1、数字在升序数组中出现的次数2、二叉搜索树的第k个节点3、二叉树的深度4、数组中只出现一次的两个数字5、和为S的两个数字6、左旋转字符串7、滑动窗口的最大值8、扑克牌顺子9、孩子们的游戏(圆圈中最后剩下的数)10、买卖股票的最好时机(一)刷题…...
若依ruoyi——手把手教你制作自己的管理系统【二、修改样式】
阿里图标一( ̄︶ ̄*)) 图片白嫖一((* ̄3 ̄)╭ ********* 专栏略长 爆肝万字 细节狂魔 请准备好一键三连 ********* 运行成功后: idea后台正常先挂着 我习惯用VScode操作 当然如果有两台机子 一个挂后台一个改前端就更好…...
2023.2.14每日一题——455. 分发饼干
每日一题题目描述解题核心解法一:双指针题目描述 题目链接:455. 分发饼干 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],…...
MySQL入门篇-MySQL常用字符函数小结
备注:测试数据库版本为MySQL 8.0 这个blog我们来聊聊常见的字符函数 函数名函数用途UPPER()返回大写的字符LOWER()返回小写的字符LTRIM()左边去掉空格TRIM()去掉空格RTRIM()右边去掉空格SPACE()返回指定长度的空格CONCAT()连接字符串CONCAT_WS()指定分隔符连接字符串CHAR_LEN…...
解决不同影像裁剪后栅格数据行列不一致问题
前言在处理栅格数据时,尽管用同一个矢量文件裁剪栅格数据,不同数据来源的栅格行列数也会出现不一致的情况。如果忽略或解决不好,会导致后续数据处理出现意想不到的误差或错误,尤其是利用编程实现数据处理时。因此,应当…...
visual studio2022配置opencv
标题:在vs下配置使用opencv 流程: 1、下载安装opencv 2、添加环境变量 3、vs中配置属性 4、使用 5、可能遇到的报错和解决 1、 下载安装opencv 官网下载地址: https://opencv.org/releases/ 我这里是windows环境,所以选择点击w…...
什么是销售管理?销售管理的五大职能
销售管理听起来很简单,似乎只是负责销售并确保客户满意,但事实上,它远不止于此。 销售管理的实际职能包括监督销售团队的工作,制定计划和设定目标,通常还包括确保销售流程的效率以获得最佳业务结果。 什么是销售管理…...
[CVPR‘22] EG3D: Efficient Geometry-aware 3D Generative Adversarial Networks
paper: https://nvlabs.github.io/eg3d/media/eg3d.pdfproject: EG3D: Efficient Geometry-aware 3D GANscode: GitHub - NVlabs/eg3d总结: 本文提出一种hybrid explicit-implicit 3D representation: tri-plane hybrid 3D representation,该方法不仅有…...
Learning C++ No.9【STL No.1】
引言: 北京时间:2023/2/13/18:29,开学正式上课第一天,直接上午一节思想政治,下午一节思想政治,生怕我们……,但,我深知该课的无聊,所以充分利用时间,把我的小…...
Apifox推荐-django后台验证token配置
最近事情很多,但是我还是想写一片推荐apifox的文章。 优秀的UI,清晰地逻辑,丰富的功能。对于我们这种业余选手来说,他真的很便利。 更新新版后有了更多贴心的功能,让你感觉他是一个有温度的工具。 最重要的是…...
SAS应用入门学习笔记6
SQL (SAS): Features: 1)不需要在每个query中重复调用每个SQL; 2)每个statement都是独立去完成的; 3)我们是没有proc print和proc sort语句的;(order by) key synta…...
【3D目标检测】Pseudo-Stereo for Monocular 3D Object Detection in Autonomous Driving
目录概述细节背景与整体流程图像级别生成特征级别生成损失函数学习深度感知的特征概述 本文是基于单目图像的3D目标检测方法。 【2021】【MonoDLE】 研究的问题: 能否借助立体图像检测算法提高单目图像检测的效果如何实现右侧图像的生成 解决的方法: 受启发于伪…...
git 常用命令之 git branch
大家好,我是 17。 新建 git 分支 分支是并行开发的基础。分支名称的本质是对分支最后一个提交的引用。分支有多个,但 HEAD 只有一个,可以认为 HEAD 是"current branch"(当下的分支)。当你用git switch切换分支的时候,…...
Oracle数据泵
Oracle 数据泵:概览 作为一个基于服务器的用于高速移动数据与元数据的工具, Oracle 数据泵具有以下特点: •可通过 DBMS_DATAPUMP 调用 •可提供以下工具: – expdp – impdp – 基于 Web 的界面 •提供四种数据移动方法ÿ…...
ACWING寒假每日一题python
ACWING寒假每日一题 一、孤独的照片 一个点一个点的来看,比如对于GHGHG中间的G,找到他的左边的G,以及右边的G的位置,l,r分别等于1,答案就要多加上11 但是如果对于 GHHGHHG 中间的G,我们可以看到l,r等于2&a…...
御黑行动来袭--助力三月重保,构筑安全防线!
三月重保在即,重要网站及业务系统“零风险 零事故”是终极目标,作为业界网络安全实战派“老兵”--知道创宇将一如既往,为您提供重保期间“万无一失”的重要网站及业务系统防护。 值此三月重保的重要备战期,知道创宇推出由主力产品…...
JavaScript HTML DOM 元素 (节点)
HTML DOM 是指 HTML 文档对象模型,它是一种用于创建和处理 HTML 页面的标准 API。在 JavaScript 中,HTML DOM 可以被用来操作和修改网页的内容和结构。在本篇文章中,我们将详细探讨 JavaScript HTML DOM 元素 (节点)的作用以及在实际工作中的…...
mybatis-plus ---2
mybatis-plus插件 官网地址 分页插件 MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能 配置并使用自带分页插件 Configuration MapperScan("com.itzhh.mapper")//可以将主类中的注解移到此处 public class MybatisPlusConfig {Beanpublic …...
如何在Qt中设置背景图片,且不覆盖其它控件
正常情况,我们直接通过在样式表里设置背景图片会出现背景图片覆盖其它控件的情况,比如下面操作: 首先右击空白处,点击改变样式表。 然后选择background-image 然后点击铅笔图标 之后我们要先添加前缀,也就是我们…...
PMP考前冲刺2.14 | 2023新征程,一举拿证
承载2023新一年的好运让我们迈向PMP终点一起冲刺!一起拿证!每日5道PMP习题助大家上岸PMP!!!PMP项目管理题目1-2:1.公司了解到一个项目机会,领导让之前做过类似项目的项目经理报告一个粗略的成本…...
feign进行文件上传报错解决方案及有多个入参时的注意事项
一、情景回顾1、简单的文件上传的接口/*** 文件上传MultipartFile格式** param multipartFile 源文件* param filename 自定义文件名称,允许为空,为空时直接从源文件中拿* return*/RequestMapping("/uploadFileForMultipartFile")LogModuleAnn…...
java 枚举类型enum的用法详解
Java Enum原理 public enum Size{ SMALL, MEDIUM, LARGE, EXTRA_LARGE }; 实际上,这个声明定义的类型是一个类,它刚好有四个实例,在此尽量不要构造新对象。 因此,在比较两个枚举类型的值时,永远不需要调用equals方法…...
网站建设灬金手指下拉/揭阳市seo上词外包
我现在的公司是自己和几个朋友创办的(大学生自主创业).人数不是很多,每个人负责一个版块.我们的经理本来开始也是专注技术的,后来因为公司的发展,他的经理基本上转到管理方面去了,因此在web技术方面就我孤单一个人了.现在觉得前途太迷茫了.本人是从学习asp开始的.然…...
怎样使自己做的网站上线/青岛关键词排名提升
PostgreSQL实现了表继承,这对数据库设计者来说是一种有用的工具(SQL:1999及其后的版本定义了一种类型继承特性,但和这里介绍的继承有很大的不同)。让我们从一个例子开始:假设我们要为城市建立一个数据模型。每一个州有很多城市,但…...
自己做的网站可以查看谁访问吗/网店营销策略有哪些
首先我们知道一个数开7 8次方过后就歇逼了 那我们是否可以维护一个区间的flag标记 表示是否一个区间里的数都是一样的 那开根就到这儿就行了(似乎在口胡?) 然后我们看一组数据 2 3 2 3 2 3 然后整体加6然后开根号,又会变回2 3 2 3…...
企业网站建设方案包含/网站优化费用报价明细
这个知识点出现在我的期末试卷上,由于当时对这个概念掌握的不清楚,3分没了。。找了很多其他的博客,这一方面的解释很少,所以在这里写一篇吧,可能自己解释的也不算清楚全面,所以请各位读者只是当做一个参考吧…...
武安网站建设/雅虎搜索引擎中文版
公司举办了关于新技术的培训,提到了“大数据”这个对我而言火了有几年的概念。所以,有必要当好“小学生”了,不懂的字该怎么办,查字典。不懂的概念怎么办,学习呗! --------------------------------------…...
网站如何做响应/蜜雪冰城网络营销案例分析
一、这里的优化主要是指对nginx的配置优化,一般来说nginx配置文件中对优化比较有作用的主要有以下几项: nginx进程数,建议按照cpu数目来指定,一般跟cpu核数相同或为它的倍数。 worker_processes 8; 为每个进程分配cpu,…...