详解静态、动态代理以及应用场景
一篇不太一样的代理模式详解,仔细阅读,你一定会获取不一样的代理见解,而不是人云亦云。
查看了社区里关于代理模式描述,发现很多博客千篇一律甚至存在共性错误,写此文提出自己对代理的见解。
- 静态代理
- 动态代理
- 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还支持学术翻译润色、查重及语言改写等服务…...

MacOS下安装和配置Nginx
一、安装brew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"按回车后,根据提示操作:输入镜像序号 --> 输入Y,回车等待brew安装完成即可。 在终端输入brew -v后,会提示…...

采用UWB(超宽频)技术开发的java版智慧工厂定位系统源码
室内定位系统源码,采用UWB定位技术开发的智慧工厂定位系统源码 技术架构:单体服务 硬件(UWB定位基站、卡牌) 开发语言:java 开发工具:idea 、VS Code 前端框架:vue 后端框架:s…...

【2023华为OD笔试必会20题--C语言版】《04 日志采集系统》——数组
本专栏收录了华为OD 2022 Q4和2023Q1笔试题目,100分类别中的出现频率最高(至少出现100次)的20道,每篇文章包括原始题目 和 我亲自编写并在Visual Studio中运行成功的C语言代码。 仅供参考、启发使用,切不可照搬、照抄,查重倒是可以过,但后面的技术面试还是会暴露的。✨✨…...

MySQL数据库——MySQL修改存储过程(ALTER PROCEDURE)
在实际开发过程中,业务需求修改的情况时有发生,所以修改 MySQL 中的存储过程是不可避免的。 MySQL 中通过 ALTER PROCEDURE 语句来修改存储过程。下面将详细讲解修改存储过程的方法。 MySQL 中修改存储过程的语法格式如下: ALTER PROCEDURE…...

ASEMI代理ADV7125JSTZ330原装ADI车规级ADV7125JSTZ330
编辑:ll ASEMI代理ADV7125JSTZ330原装ADI车规级ADV7125JSTZ330 型号:ADV7125JSTZ330 品牌:ADI/亚德诺 封装:LQFP-48 批号:2023 引脚数量:48 工作温度:-40C~85C 安装类型:表面…...

86盒IP对讲一键报警器
86盒IP对讲一键报警器 86盒IP对讲一键报警器:革命性保障生命安全的利器! 随着科技的飞速发展,我们的生活变得越来越方便和智能化。而86盒IP对讲一键报警器更是在这种背景下应运而生。这款产品不仅无缝对接各种手机APP,也可以在智…...

【高数+复变函数】傅里叶积分
文章目录 【高数复变函数】傅里叶积分2. 傅里叶积分2.1 复数形式积分公式2.2 三角形式 上一节: 【高数复变函数】傅里叶级数 【高数复变函数】傅里叶积分 2. 傅里叶积分 在上一节中,我们知道了傅里叶级数的基本知识,其中,周期为…...

【Leetcode】241. 为运算表达式设计优先级
241. 为运算表达式设计优先级(中等) 解法一:分治法 对于这道题,加括号其实就是决定运算次序,所以我们可以把加括号转化为,「对于每个运算符号,先执行处理两侧的数学表达式,再处理此…...

torch两个向量除法,对于分母向量中的元素为0是设置为1,避免运算错误
在gpu运行时,如果在进行两个向量除法的时候,对于分母向量中的元素为0是设置为1,避免运算错误。 可以使用torch的division函数以及clamp函数来解决这个问题。具体步骤如下: 使用division函数将分子向量除以分母向量。 使用clamp函…...

NodeJs 最近各版本特性汇总
(预测未来最好的方法就是把它创造出来——尼葛洛庞帝) NodeJs 官方链接 github链接 V8链接 Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模…...