详解静态、动态代理以及应用场景
一篇不太一样的代理模式详解,仔细阅读,你一定会获取不一样的代理见解,而不是人云亦云。
查看了社区里关于代理模式描述,发现很多博客千篇一律甚至存在共性错误,写此文提出自己对代理的见解。
- 静态代理
- 动态代理
- JDK
- CGLib
- 静态代理 VS 动态代理
- 直观的看到动态代理的模样
- 那种只通过接口就能实现功能的技术是如何实现的
女朋友问我什么是代理,静态代理与动态代理的区别是什么,各有什么优势呢?什么场景下适合静态代理,什么场景该使用动态代理呢?真的如网上所说的,静态代理一无是处吗? 作为一个合格的男朋友,必须给她安排上,这就说道说道。
一、静态代理
1.1 静态代理架构图

静态代理类图.png
角色:
- 接口
- 被代理实现类
- 代理实现类
核心在于代理对象与被代理对象都需要实现同一个Interface接口,这一点也非常好理解,代理嘛 就是要代理被代理对象的所有方法。
1.2 代码案例
代码比较简单,使用静态代理为一个只有加法功能的计算器在计算前后打印日志:
/*** 静态代理*/
public class StaticProxy {/*** 接口*/interface Factory {int plus(int one, int two);}/*** 被代理对象*/static class PlusFactory implements Factory {@Overridepublic int plus(int one, int two) {return one + two;}}/*** 代理对象*/static class ProxyFactory implements Factory {private Factory beAgentFactory;public ProxyFactory(Factory beAgentFactory) {this.beAgentFactory = beAgentFactory;}@Overridepublic int plus(int one, int two) {try {System.out.println("before plus");return beAgentFactory.plus(one, two);} finally {System.out.println("after plus");}}}/*** 测试** @param args*/public static void main(String[] args) {// 被代理对象PlusFactory beAgentFactory = new PlusFactory();ProxyFactory proxyFactory = new ProxyFactory(beAgentFactory);int result = proxyFactory.plus(1, 2);System.out.println(result);}}**// 测试结果
before plus
after plus
3**
1.3 静态代理的缺点 以及对现有博客的抨击
目前网上关于静态代理的优缺点分析都存在着一个共性的错误。我们只有在深刻的理解静态代理与其应用场景才能发现这些错误描述。
1.3.1 千篇一律的认知错误
在国内代码社区中,搜索关于静态代理的缺点文章,几乎千篇一律的指责到:程序员要手动为每一个被代理类编写对应的代理类,如果当前系统已经有成百上千个类,工作量太大了。
真的一定是这样吗?
在上述demo中,ProxyFactory类构造函数会接受一个Factory接口的实现类进行代理。因此就算此处需要新增一个被代理对象,理论上也不需要再去做一个代理对象了,因为这些被代理类都是Factory类型。这算是Java最基础的知识了!
那么什么场景下,当新创建一个被代理类时,一定需要写一个与之对应的代理类呢?
我们还用上面的demo来看,上面的demo中代理类只干了一件事:计算的前后分别打印一行日志。假设我们现在又需要写一个被代理类:HttpPlusFactory,我们希望在完成加法之后将结果通过HTTP发送给其他系统,这时我们原有的代理类ProxyFactory就显得不够用了,我们需要新建一个专门的HttpProxyFactory代理才行。
简而言之,代理存在的目的是想在不修改原有的代码为前提实现一个共性需求。即将共性的需求放入代理中实现,假设我们有不同的共性需求,我们才需要抽象出不同的代理对象。这一段话有点绕,但是我希望你能明白含义。
1.3.2 静态代理真正的缺点
抨击完国内千篇一律的错误之后,我们来谈谈静态代理有什么不太方便的地方。
-
代理类编写麻烦:
这个代理对象与被代理对象一样要实现同一个接口,如果接口中有100个方法那么代理对象就得实现100个方法。
-
一些只有接口没有被代理类的场景无法使用静态代理:
静态代理中,一定是要存在一个被代理对象,这对于一些只通过接口就能完成业务功能的需求很不友好,譬如:
MyBatis、Feign、Dubbo。
二、动态代理
动态代理,对于很多框架的实现非常友好,其诞生的目的就是让我们不去写代理对象(JDK或者CGlib帮助我们自动生成)。
2.1 实现方式
-
JDK
基于
Interface生成实现类完成代理 -
Cglib
基于
Class生成子类完成代理
2.1.1 JDK Demo
/*** 动态代理 - 手动编写被代理类*/
public class DynamicProxy {/*** JDK动态代理基于接口*/interface Factory {int plus(int one, int two);}/*** 被代理对象*/static class PlusFactory implements Factory {@Overridepublic int plus(int one, int two) {return one + two;}}/*** 代理对象*/static class ProxyFactory implements InvocationHandler {private Factory beAgentFactory;public ProxyFactory(Factory beAgentFactory) {this.beAgentFactory = beAgentFactory;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {System.out.println("before plus");return method.invoke(beAgentFactory, args);} finally {System.out.println("after plus");}}}public static void main(String[] args) {// 1\. 被代理对象Factory beAgentFactory = new PlusFactory();// 2\. 生成动态代理ProxyFactory proxyFactory = new ProxyFactory(beAgentFactory);Factory proxyInstance = (Factory) Proxy.newProxyInstance(proxyFactory.getClass().getClassLoader(), new Class[]{Factory.class}, proxyFactory);// 3.调用方法int plus = proxyInstance.plus(1, 2);System.out.println(plus);}
}**// 测试结果
before plus
after plus
3**
看完这个demo之后,你可能会想,这和静态代理有什么区别呢?还是要有接口、被代理类、代理类三个角色。但你要知道,如果Factory接口中如有100个抽象方法,那么代理类中只需要有一个invoke方法即可!这是和静态代理中的代理类有着本质的差别,这主要得益于动态代理中的反射机制。
当然了,如果只是这样的话,动态代理的优势还不足以让你折服,我们再来看下面这个例子:
/*** 动态代理 - 不需要手写被代理类*/
public class DynamicProxy {/*** JDK动态代理基于接口*/interface Factory {int plus(int one, int two);}static class ProxyFactory implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {System.out.println("before plus");if (method.getName().equals("plus")) {return (int) args[0] + (int) args[1];}return null;} finally {System.out.println("after plus");}}}public static void main(String[] args) {// 2\. 生成动态代理ProxyFactory proxyFactory = new ProxyFactory();Factory proxyInstance = (Factory) Proxy.newProxyInstance(proxyFactory.getClass().getClassLoader(), new Class[]{Factory.class}, proxyFactory);// 3.调用方法int plus = proxyInstance.plus(1, 2);System.out.println(plus);}
}**// 测试结果
before plus
after plus
3**
在上面这个例子中,我们直接去掉了被代理对象,而是将业务抽象到了代理类中。想一想这还是得益于反射机制吧,这时候你再对比对比静态代理的代码,是不是发现静态代理就没法这么玩了。
2.1.2 Cglib Demo
/*** Cglib实现动态代理**/
public class CglibProxy {/*** 被代理对象*/static class PlusFactory {public int plus(int one, int two) {return one + two;}}/*** Cglib Callback*/static class ProxyCallBack implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {try {System.out.println("before calculate");return methodProxy.invokeSuper(o, objects);} finally {System.out.println("after calculate");}}}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(PlusFactory.class);enhancer.setCallback(new ProxyCallBack());PlusFactory proxy = (PlusFactory) enhancer.create();int result = proxy.plus(1, 2);System.out.println(result);}}
可以看到Cglib实现方式中,重点在于CallBack中,也就是此处的ProxyCallBack,其内部的intercept方法参数中也有Method、参数等信息,因此使用上和JDK Proxy感觉非常相似。
2.2 JDK Proxy VS Cglib Proxy
- JDK Proxy是基于接口生成实现类完成代理,Cglib Proxy是基于Class生成子类完成代理。所有Cglib中被代理类中的方法不能有
private、final修饰。而JDK Proxy就没有此限制,因为Java语言中接口中的方法天然不能使用private、final进行修饰。 - 速度,这是网上传的比较多的一种比较,因为我没有做过相关实验,因此不在此定论。
三、静态代理 VS 动态代理
3.1 区别
- 静态代理一定要编写至少一个被代理类与一个代理类,而动态代理可以不编写任何被代理类与代理类
- 静态代理编写麻烦,因为代理类与被代理类需要实现同一个接口,如果接口有100个方法,那么就需要实现100个方法,而动态代理不需要,动态代理底层使用反射极大减少了开发量,将100个方法压缩成一个
invoke方法即可。

静态代理VS动态代理.drawio.png
3.2 使用场景
3.2.1 静态代理
适合不存在共性需求的场景,比如被代理类中有100个方法,代理对象中自然也有100个方法,但是这100个方法没有共性需求,可能第一个方法是打印日志,第二个方法需要发送HTTP… 那么这时候就适合用静态代理了(假设一定需要使用代理的话)。
3.2.2 动态代理
动态代理非常适合框架底层,并且共性需求很大,参考MyBatis、Feign、Dubbo。
四、直观的看到动态代理的模样
为了加深动态代理的理解,这里以JDK动态代理为例,将上述2.1.1 JDK Demo中JDK生成的代理类打印出来。
在main方法中添加添加如下代码,最后会在项目根路径下保存生成的动态代理类:
//JDK1.8及以前的版本
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//JDK1.8以后的版本
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
生成的代理类如下:
final class $Proxy0 extends Proxy implements Factory {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int plus(int var1, int var2) throws {try {// 这里的super是Proxy,里面的h属性,就是我们实现的InnvocationHandler类return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});} catch (RuntimeException | Error var4) {throw var4;} catch (Throwable var5) {throw new UndeclaredThrowableException(var5);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.example.demo.dynamic.DynamicProxy$Factory").getMethod("plus", Integer.TYPE, Integer.TYPE);m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}
可以看到JDK生成的代理类是实现了接口,在实现的方法中调用了父类Proxy中的InvocationHandler的invoke方法,并且将method信息、参数信息都传过去。
五、那种只通过接口就能实现功能的技术是如何实现的
目前市面上只通编写接口就能实现功能的框架有很多,比如:MyBatis、Feign等。
那么你是否也折服于他们只通过Interface就能完成功能的能力?但只要你熟练掌握动态代理的使用与原理,理解这些框架并不难。
这些框架大致的实现思路为:
- 肯定使用JDK动态代理,因为只有接口,没有实现类;
- 依赖Spring的生命周期钩子,对需要生成动态代理的接口进行代理,并将生成好的代理类放入
Spring IOC中以提供后续业务的使用。
这里我们开发一个Demo,需求是只需编写Interface接口配合上一些注解完成HTTP发送。
Demo如下:
-com.example.demo-app-baseinterface-annHttpExecute.javaHttpService.java-proxyJDKProxy.java-scannerClassScanner.java-serviceBaseInterfaceBusiness.java
- 定义注解:
@Documented
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpService {
}
@Documented
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpExecute {String url();
}
- Spring生命周期钩子,扫描所有使用@HttpService注解修饰的类,并创建其代理对象并存入IOC容器中
@Component
public class ClassScanner implements BeanDefinitionRegistryPostProcessor {private final String DEFAULT_RESOURCE_PATTERN = "**/*.class";private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "com" + "/" + DEFAULT_RESOURCE_PATTERN;Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();if (annotationMetadata.getAnnotationTypes().contains(HttpService.class.getName())) {Class<?> aClass;aClass = Class.forName(annotationMetadata.getClassName());if (aClass != null) {configurableListableBeanFactory.registerSingleton(toLowerCaseFirstOne(aClass.getSimpleName()),// 使用JDK动态创建代理对象JDKProxy.getInstance(aClass));System.out.println("扫描到的 HttpService 接口" + aClass.getSimpleName());}} else {continue;}}}} catch (IOException e) {} catch (Exception e) {}}public static String toLowerCaseFirstOne(String s) {if (Character.isLowerCase(s.charAt(0))) {return s;} else {return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();}}}
- InvocationHandler实现类:
public class JDKProxy implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 对象的所有Object类,直接通过if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}HttpExecute annotation = method.getAnnotation(HttpExecute.class);System.out.println("发送HTTP请求: " + annotation.url());return "";}public static <T> T getInstance(Class<T> interfaces) {return (T) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{interfaces}, new JDKProxy());}}
- 业务应用
@HttpService
public interface BaseInterfaceBusiness {@HttpExecute(url = "<https://xxxx.com>")void reduceInventory();}
- 测试
@SpringBootTest
public class DynamicAppTest {@Resourceprivate BaseInterfaceBusiness baseInterfaceBusiness;@Testpublic void baseInterfaceTest() {baseInterfaceBusiness.reduceInventory();}}// 结果:
// 发送HTTP请求: https://xxxx.com相关文章:
详解静态、动态代理以及应用场景
一篇不太一样的代理模式详解,仔细阅读,你一定会获取不一样的代理见解,而不是人云亦云。 查看了社区里关于代理模式描述,发现很多博客千篇一律甚至存在共性错误,写此文提出自己对代理的见解。 静态代理动态代理 JDKCGLi…...
ChatGLM-6B本地cpu部署
ChatGLM-6B是清华团队研发的机器人对话系统,类似ChatGPT,但是实际相差很多,可以当作一个简单的ChatGPT。 ChatGLM部署默认是支持GPU加速,内存需要32G以上。普通的机器无法运行。但是可以部署本地cpu版本。 本地部署,需…...
算法修炼之练气篇——练气七层
博主:命运之光 专栏:算法修炼之练气篇 前言:每天练习五道题,炼气篇大概会练习200道题左右,题目有C语言网上的题,也有洛谷上面的题,题目简单适合新手入门。(代码都是命运之光自己写的…...
vscode常用快捷方式
基本编辑 Ctrl X:剪切当前行或选定内容 Ctrl C:复制当前行或选定内容 Ctrl V:粘贴当前行或剪切板内容 Ctrl Z:撤销上一步操作 Ctrl Y:恢复上一步撤销的操作 Ctrl F:在当前文件中查找内容 Ctrl H&am…...
如何压缩mp3文件大小,5分钟学会4种方法
如何压缩mp3文件大小?我们在开车的时候都很喜欢听歌,一般歌曲库里的mp3文件都很多,小编的就有上千首。如果我们还想要增加更多mp3文件,有时候就会出现内存不足的情况啦。所以我们需要压缩mp3文件大小,这样才能在我们手…...
从0搭建Vue3组件库(十二):引入现代前端测试框架 Vitest
Vitest 是个高性能的前端单元测试框架,它的用法其实和 Jest 差不多,但是它的性能要优于 Jest 不少,还提供了很好的 ESM 支持,同时对于使用 vite 作为构建工具的项目来说有一个好处就是可以公用同一个配置文件vite.config.js。因此本项目将会使用 Vitest 作为测试框架。 安装 …...
使用Handler创建一个Android秒表应用
本文所有代码都放在以下链接中:https://github.com/MADMAX110/Stopwatch 0、应用是一个有活动、布局和其他资源组成的集合。其中一个活动是应用的主活动。每个应用都有一个主活动,在文件AndroidManifest.xml中指定。 1、默认地,每个应用都在…...
node-sass安装失败解决方法总结
node-sass 安装失败的原因 npm 安装 node-sass 依赖时,会从 github.com 上下载 .node 文件。由于国内网络环境的问题,这个下载时间可能会很长,甚至导致超时失败。 解决方法一:使用淘宝镜像源(推荐) npm …...
C++特殊类设计
文章目录 1.设计一个类,不能被拷贝2.设计一个类,只能在堆上创建对象3.设计一个类,只能在栈上创建对象4.设计一个类,不能被继承5.设计一个类,只能创建一个对象5.1 单例模式5.2 饿汉模式5.3 懒汉模式5.4 两种模式的析构函…...
常用的python gpu加速方法
在使用 PyCharm进行机器学习的时候,我们常常需要自己创建一些函数,这个过程中可能会浪费一些时间,在这里,我们为大家整理了一些常用的 Python加速方法,希望能给大家带来帮助。 在 Python中,我们经常需要创建…...
SpringCloud-Gateway
什么是网关? 网关是一个服务,是访问内部系统的唯一入口,提供内部服务的路由中转,额外还可以在此基础上提供如身份验证、监控、负载均衡、限流、降级与应用检测等功能。 Spring Cloud Gateway 与 Zuul 对比 zuul1.x与zuul2.x Zu…...
【C++ qt4】操作json学习笔记
本博文源于笔者在学习c qt4操作json文件,qt4不支持json,里面的函数是json.h与jsoncpp.cpp我已经附在文末,大家可复制重命名用,里面的案例可以自己拿来敲或者直接copy也行.,一定利用好目录拖动,不然很长。 文章目录 1.从…...
【牛客刷题专栏】0x25:JZ24 反转链表(C语言编程题)
前言 个人推荐在牛客网刷题(点击可以跳转),它登陆后会保存刷题记录进度,重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏:个人CSDN牛客刷题专栏。 题目来自:牛客/题库 / 在线编程 / 剑指offer: 目录 前言问…...
useEffect
useEffect 1.依赖项是什么?2.useEffect怎么知道依赖项数组发生了改变?3.依赖项的改变会导致无限渲染吗?4.使用 Object.is 来比较新/旧 state 是否相等,浅比较?5.为什么要用浅比较,而不用深比较呢࿱…...
如何利用splice()和slice()方法操作数组
如何利用splice()和slice()方法操作数组 前言splice()是什么,有什么用?怎么用?slice()是什么,有什么用?怎么用?splice和slice方法的区别小结 前言 splice&am…...
一文读懂ChatGPT(全文由ChatGPT撰写)
最近ChatGPT爆火,相信大家或多或少都听说过ChatGPT。到底ChatGPT是什么?有什么优缺点呢? 今天就由ChatGPT自己来给大家答疑解惑~ 全文文案来自ChatGPT! 01 ChatGPT是什么 ChatGPT是一种基于人工智能技术的自然语言处理系统&…...
如何提升应届生职场竞争力
引言 对于应届毕业生来说,进入职场是既令人兴奋又具有挑战性的。面对竞争激烈的就业市场,提高自身的职场竞争力对于应届生来说尤为重要。本文旨在为应届生提供有价值的见解和实用的策略,帮助他们提升职场竞争力,增加在就业市场中的…...
David Silver Lecture 5: Model-Free Control
1 Introduction 1.1 内容 上一章是对一个unknown MDP进行value function的预测,相当于policy evaluation。这一章是对unknown MDP找到一个最优的policy, optimise value function. 1.2 On and Off-Policy Learning On-policy learning learn on the…...
MySQL-----事务管理
文章目录 前言一、什么是事务二、为什么会出现事务三、事物的版本支持四、事物的提交方式五、事务常见的操作方式六、事务隔离级别如何理解隔离性1隔离级别查看与设置隔离性读未提交【Read Uncommitted】读提交【Read Committed】可重复读【Repeatable Read】串行化【serializa…...
chatGPT润色中英论文软件-文章修改润色器
chatGPT可以润色英文论文吗? ChatGPT可以润色英文论文,它具备自动纠错、自动完善语法和严格全面的语法、句法和内容结构检查等功能,可以对英文论文进行高质量的润色和优化。此外,ChatGPT还支持学术翻译润色、查重及语言改写等服务…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
