详解静态、动态代理以及应用场景
一篇不太一样的代理模式详解,仔细阅读,你一定会获取不一样的代理见解,而不是人云亦云。
查看了社区里关于代理模式描述,发现很多博客千篇一律甚至存在共性错误,写此文提出自己对代理的见解。
- 静态代理
- 动态代理
- 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还支持学术翻译润色、查重及语言改写等服务…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
