【深入理解设计模式】代理设计模式

news2025/1/16 16:06:19

在这里插入图片描述

代理设计模式:

代理设计模式是一种结构型设计模式,它允许你提供一个替代物或占位符来控制对其他对象的访问。在代理模式中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式,因为该模式涉及类和对象的组合。

概述

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

Java中的代理按照代理类生成时机不同又分为静态代理动态代理静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。

结构

代理(Proxy)模式分为三种角色:

  • 抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

代理模式的一些常见用途包括:

  • 远程代理(Remote Proxy):在一个不同的地址空间中代表一个对象。这种方式可以隐藏对象存在于不同地址空间的事实。

  • 虚拟代理(Virtual Proxy):根据需要创建昂贵的对象。比如,当一个图像文件非常大时,可以使用虚拟代理来延迟加载(Lazy Loading)图像,只有当真正需要显示图像时才加载。

  • 保护代理(Protection Proxy):控制对对象的访问权限。这种方式可以根据调用者的身份来限制对某些方法的访问,比如检查调用者是否有足够的权限。

  • 缓存代理(Caching Proxy):为昂贵操作的结果提供缓存,以便在后续请求中直接使用缓存结果,提高性能。

静态代理

静态代理是代理模式的一种形式,它在编译时就已经确定了代理对象和真实对象的关系,即在程序运行前就已经确定了代理对象和真实对象的关系,因此称为静态代理。

在静态代理中,代理类和真实类通常实现同一个接口或继承同一个父类,并且代理类持有真实类的引用。代理类负责将调用转发给真实类,同时可以在调用前后执行一些额外的逻辑,如权限验证、日志记录、性能监控等。

// 抽象主题
interface Subject {
    void request();
}

// 真实主题
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 代理
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        // 在调用真实对象前后可以添加一些额外的逻辑
        System.out.println("Proxy: Before requesting.");
        realSubject.request();
        System.out.println("Proxy: After requesting.");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        
        // 通过代理对象调用真实对象的方法
        proxy.request();
    }
}

静态代理的优点在于:

  • 简单易理解:代理对象可以在不修改真实对象的情况下添加额外的功能,易于理解和实现。
  • 对真实对象的访问进行控制:代理对象可以控制对真实对象的访问权限。

但静态代理也存在一些缺点:

  • 静态代理每次新增功能都需要编写新的代理类,导致类的数量增加。
  • 在代理类中硬编码了对真实对象的引用,一旦真实对象发生变化,代理类也需要相应修改。

静态代理是代理模式的一种简单实现方式,适用于代理对象相对固定且功能不复杂的场景。

动态代理

JDK动态代理

动态代理是一种在运行时生成代理对象的机制,与静态代理不同,它不需要在编译时就确定代理对象和真实对象的关系,而是在程序运行时动态创建代理对象。Java 中的动态代理主要依赖于 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。
以下是一个基于JDK的动态代理的示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Subject {
    void request();
}

// 真实主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 实现 InvocationHandler 接口
class DynamicProxy implements InvocationHandler {
    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在调用真实对象的方法前后添加额外的逻辑
        System.out.println("DynamicProxy: Before requesting.");
        Object result = method.invoke(target, args);
        System.out.println("DynamicProxy: After requesting.");
        return result;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建真实对象
        RealSubject realSubject = new RealSubject();
        
        // 创建动态代理对象
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                new DynamicProxy(realSubject));
        
        // 通过代理对象调用真实对象的方法
        proxy.request();
    }
}

在上面的示例中,DynamicProxy 类实现了 InvocationHandler 接口,在 invoke 方法中实现了代理逻辑。通过 Proxy.newProxyInstance 方法创建了代理对象,并传入了 DynamicProxy 对象作为 InvocationHandler 的实现。最后,通过代理对象调用真实对象的方法。
示例二:

/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 抽象主题 - 买票接口
 */
public interface SellTicket {
    void sell();
}
/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 具体主题(被代理对象) - 火车站类
 */
public class TrainStation implements SellTicket {

    @Override
    public void sell() {
        System.out.println("火车站卖票");
    }
}
/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 代理对象工厂,用来创建代理对象
 */
public class ProxyFactory {

    // 目标代理对象
    private Object target = new TrainStation();

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public SellTicket getProxy() {
        /*
            newProxyInstance方法的三个参数:
            1.ClassLoader loader,  类加载器,用于加载代理类,使用真实对象的类加载器即可
            2.Class<?>[] interfaces,真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
            3.InvocationHandler h  代理对象的调用处理程序
         */
        //使用Proxy方法获取代理对象
        return (SellTicket) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    /*
                        InvocationHandler中invoke方法参数说明:
                            proxy : 代理对象
                            method : 对应于在代理对象上调用的接口方法的 Method 实例
                            args : 代理对象调用接口方法时传递的实际参数
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("DynamicProxy: Before requesting.");
                        Object result = method.invoke(target, args);
                        System.out.println("DynamicProxy: After requesting.");
                        return result;
                    }
                }
        );
    }
}
/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 客户端 - 测试类
 */
public class Client {
    public static void main(String[] args) {
        // 创建目标代理对象
        TrainStation station = new TrainStation();
        // 将目标代理对象传入代理工厂,获取代理工厂对象
        ProxyFactory proxyFactory = new ProxyFactory(station);
        // 根据代理工厂对象获取代理对象
        SellTicket proxy = proxyFactory.getProxy();
        // 使用代理对象对原方法进行扩展和增强
        proxy.sell();
    }
}

使用了动态代理,我们思考下面问题:

  • ProxyFactory是代理类吗?

    ProxyFactory不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类。通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)查看代理类的结构:

    package com.sun.proxy;
    
    import com.itheima.proxy.dynamic.jdk.SellTickets;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements SellTickets {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler invocationHandler) {
            super(invocationHandler);
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                return;
            }
            catch (NoSuchMethodException noSuchMethodException) {
                throw new NoSuchMethodError(noSuchMethodException.getMessage());
            }
            catch (ClassNotFoundException classNotFoundException) {
                throw new NoClassDefFoundError(classNotFoundException.getMessage());
            }
        }
    
        public final boolean equals(Object object) {
            try {
                return (Boolean)this.h.invoke(this, m1, new Object[]{object});
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final String toString() {
            try {
                return (String)this.h.invoke(this, m2, null);
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final int hashCode() {
            try {
                return (Integer)this.h.invoke(this, m0, null);
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final void sell() {
            try {
                this.h.invoke(this, m3, null);
                return;
            }
            catch (Error | RuntimeException throwable) {
                throw throwable;
            }
            catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    }
    

    从上面的类中,我们可以看到以下几个信息:

    • 代理类($Proxy0)实现了SellTickets。这也就印证了我们之前说的真实类和代理类实现同样的接口。
    • 代理类($Proxy0)将我们提供了的匿名内部类对象传递给了父类。
  • 动态代理的执行流程是什么样?

    下面是摘取的重点代码:

    //程序运行过程中动态生成的代理类
    public final class $Proxy0 extends Proxy implements SellTickets {
        private static Method m3;
    
        public $Proxy0(InvocationHandler invocationHandler) {
            super(invocationHandler);
        }
    
        static {
            m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
        }
    
        public final void sell() {
            this.h.invoke(this, m3, null);
        }
    }
    
    //Java提供的动态代理相关类
    public class Proxy implements java.io.Serializable {
    	protected InvocationHandler h;
    	 
    	protected Proxy(InvocationHandler h) {
            this.h = h;
        }
    }
    
    //代理工厂类
    public class ProxyFactory {
    
       	 // 目标代理对象
      	private Object target = new TrainStation();
    
     	 public ProxyFactory(Object target) {
        	  this.target = target;
    	  }
    
        public SellTickets getProxyObject() {
            SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
                	station.getClass().getClassLoader(),
                    station.getClass().getInterfaces(),
                    new InvocationHandler() {
                        
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                            System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
                            Object result = method.invoke(station, args);
                            return result;
                        }
                    });
            return sellTickets;
        }
    }
    
    
    //测试访问类
    public class Client {
        public static void main(String[] args) {
        	TrainStation station = new TrainStation();
            //获取代理对象
            ProxyFactory factory = new ProxyFactory(station );
            SellTickets proxyObject = factory.getProxyObject();
            proxyObject.sell();
        }
    }
    

执行流程如下:

  1. 在测试类中通过代理对象调用sell()方法
  2. 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法
  3. 代理类($Proxy0)中的sell()方法中又调用了InvocationHandler接口的子实现类对象的invoke方法
  4. invoke方法通过反射执行了真实对象所属类(TrainStation)中的sell()方法
CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的开源字节码生成类库,它可以在运行时动态生成指定类的子类,并覆盖其中的方法,从而实现动态代理。相比于基于接口的动态代理,CGLIB 动态代理不要求目标对象必须实现接口,因此更加灵活。

CGLIB 动态代理的核心思想是利用字节码生成技术,通过生成目标类的子类,并在子类中重写需要代理的方法,从而达到对目标类的代理控制。

CGLIB是第三方提供的包,所以需要引入jar包的坐标:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

以下是一个使用 CGLIB 动态代理的简单示例:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标类
class RealSubject {
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 实现 MethodInterceptor 接口
class DynamicProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在调用真实对象的方法前后添加额外的逻辑
        System.out.println("DynamicProxy: Before requesting.");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("DynamicProxy: After requesting.");
        return result;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建 Enhancer 对象,用于生成代理类
        Enhancer enhancer = new Enhancer();
        
        // 设置目标类的父类,即需要代理的类
        enhancer.setSuperclass(RealSubject.class);
        
        // 设置回调对象,即实现 MethodInterceptor 接口的对象
        enhancer.setCallback(new DynamicProxy());
        
        // 创建代理对象
        RealSubject proxy = (RealSubject) enhancer.create();
        
        // 通过代理对象调用目标类的方法
        proxy.request();
    }
}

在上面的示例中,我们使用了 CGLIB 提供的 Enhancer 类来生成代理类。通过 setSuperclass 方法设置目标类的父类,即需要代理的类;通过 setCallback 方法设置回调对象,即实现了 MethodInterceptor接口的对象,用于在代理类中增加额外逻辑;最后通过 create 方法创建代理对象。

示例二:

/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 具体主题(被代理对象) - 火车站类
 */
public class TrainStation {

    public void sell() {
        System.out.println("火车站卖票");
    }
}
/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 生成代理对象
 */
public class ProxyFactory implements MethodInterceptor {

	private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }
    
    public TrainStation getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return (TrainStation) enhancer.create();
    }

    /**
     * intercept方法参数说明:
     * @param o 代理对象
     * @param method 真实对象中的方法的Method实例
     * @param objects 实际参数
     * @param methodProxy 代理对象中的方法的method实例
     * @return 真实对象中的方法的Method返回值
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("DynamicProxy: Before requesting.");	
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("DynamicProxy: After requesting.");
        return object;
    }
}
/**
 * @author OldGj 2024/02/22
 * @version v1.0
 * @apiNote 客户端  - 测试类
 */
public class Client {
   public static void main(String[] args) {
        TrainStation station = new TrainStation();
        //创建代理工厂对象
        ProxyFactory factory = new ProxyFactory(station);
        //获取代理对象
        TrainStation proxyObject = (TrainStation) factory.getProxy();

        proxyObject.sell();
    }
}

CGLIB 动态代理相比于基于接口的动态代理更加灵活,不要求目标类必须实现接口,因此更适合对没有实现接口的类进行代理。但是需要注意的是,CGLIB 动态代理生成的代理类是目标类的子类,如果目标类是 final 类,则无法生成子类,因此无法使用 CGLIB 动态代理。

三种代理的对比

  • jdk代理和CGLIB代理

    使用CGLib实现动态代理,CGLIB 动态代理的核心思想是利用字节码生成技术,通过生成目标类的子类,并在子类中重写需要代理的方法,从而达到对目标类的代理控制。,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。

    在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果(目标类)被代理类有接口使用JDK动态代理,如果没有接口使用CGLIB代理。

  • 动态代理和静态代理

    动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

    如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题

代理模式的一些常见用途包括:

远程代理(Remote Proxy):在一个不同的地址空间中代表一个对象。这种方式可以隐藏对象存在于不同地址空间的事实。

虚拟代理(Virtual Proxy):根据需要创建昂贵的对象。比如,当一个图像文件非常大时,可以使用虚拟代理来延迟加载(Lazy Loading)图像,只有当真正需要显示图像时才加载。

保护代理(Protection Proxy):控制对对象的访问权限。这种方式可以根据调用者的身份来限制对某些方法的访问,比如检查调用者是否有足够的权限。

缓存代理(Caching Proxy):为昂贵操作的结果提供缓存,以便在后续请求中直接使用缓存结果,提高性能。

优缺点

优点:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

缺点:

  • 增加了系统的复杂度;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1470547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【谈一谈】Redis是AP还是CP?

【谈一谈】Redis是AP还是CP? 再说这个话题之前,这里的是AP和CP不是"A片"和"C骗"啊 !~哈哈哈,就离谱,博文后面我会解释下的 我说下自己对Redis的感觉,我一直很好奇Redis,不仅仅是当缓存用那么简单,包括的它的底层设计 所以,思考再三,我决定先从Redis基础开…

最简单的基于 FFmpeg 的视音频分离器

最简单的基于 FFmpeg 的视音频分离器 最简单的基于 FFmpeg 的视音频分离器正文结果工程文件下载参考链接 最简单的基于 FFmpeg 的视音频分离器 参考雷霄骅博士的文章&#xff0c;链接&#xff1a;最简单的基于FFmpeg的封装格式处理&#xff1a;视音频分离器&#xff08;demuxe…

五种多目标优化算法(MOFA、NSWOA、MOJS、MOAHA、MOPSO)性能对比(提供MATLAB代码)

一、5种多目标优化算法简介 多目标优化算法是用于解决具有多个目标函数的优化问题的一类算法。其求解流程通常包括以下几个步骤&#xff1a; 1. 定义问题&#xff1a;首先需要明确问题的目标函数和约束条件。多目标优化问题通常涉及多个目标函数&#xff0c;这些目标函数可能…

电商风控系统(flink+groovy+flume+kafka+redis+clickhouse+mysql)

一.项目概览 电商的防止薅羊毛的风控系统 需要使用 groovy 进行风控规则引擎的编写 然后其它技术进行各种数据的 存储及处理 薅羊毛大致流程 如果单纯使用 if else在业务代码中进行风控规则的编写 那么 维护起来会比较麻烦 并且跟业务系统强绑定不合适 所以一般独立成一个单…

2024年2月20日v1.0.5更新·优雅草便民工具youyacao-tools

2024年2月20日v1.0.5更新优雅草便民工具youyacao-tools apk下载 https://fenfacun.youyacao.com/tools105.apk 介绍 优雅草便民工具是一款由成都市一颗优雅草科技有限公司打造的便民查询公益工具&#xff0c;2024年1月17日正式发布v1.0.0版本&#xff0c;本工具为了方便大众免…

贪心算法---前端问题

1、贪心算法—只关注于当前阶段的局部最优解,希望通过一系列的局部最优解来推出全局最优----但是有的时候每个阶段的局部最优之和并不是全局最优 例如假设你需要找给客户 n 元钱的零钱&#xff0c;而你手上只有若干种面额的硬币&#xff0c;如 1 元、5 元、10 元、50 元和 100…

基于相位的运动放大:如何检测和放大难以察觉的运动(01/2)

基于相位的运动放大&#xff1a;如何检测和放大难以察觉的运动 目录 一、说明二、结果的峰值三、金字塔背景3.1 可操纵金字塔3.2 亚倍频程复数可控金字塔 四、基本方针4.1 1D 问题陈述4.2 一维方法4.3 实际实施说明 五、放大倍率的限制5.1 空间支持的影响5.2 频带的影响 六、推…

SpringBoot3整合Swagger3,访问出现404错误问题(未解决)

秉承着能用就用新的的理念&#xff0c;在JDK、SpringBoot、SpringCloud版本的兼容性下&#xff0c;选择了Java17、SpringBoot3.0.2整合Swagger3。 代码编译一切正常&#xff0c;Swagger的Bean也能加载&#xff0c;到了最后访问前端页面swagger-ui的时候出现404。 根据网上资料…

【计算机网络】传输层——TCP和UDP详解

文章目录 一. TCP和UDP简介二. UDP 协议详解1. UDP报文格式2. UDP的使用场景 三. TCP 协议详解1. TCP报文格式2. TCP协议的重要机制确认应答&#xff08;保证可靠传输的最核心机制&#xff09;超时重传连接管理&#xff08;三次握手、四次挥手&#xff09;&#xff01;&#xf…

React组件通讯

组件通讯 组件是一个独立的单元&#xff0c;默认情况下组件只能自己使用自己的数据。在组件化过程中&#xff0c;我们将一个完整的功能拆分成多个组件&#xff0c;便于更好的完成整个应用的功能。 Props 组件本来是封闭的&#xff0c;要接受外部数据应该可以通过Props来实现…

Jmeter学习系列之八:控制器Controllers 的入门介绍

一、Controllers 简介 Jmeter有两种类型的控制器&#xff1a;Samplers&#xff08;取样器&#xff09;和Logical Controllers&#xff08;逻辑控制器&#xff09;&#xff1b;它们驱动着测试的进行取样器&#xff1a;让jmeter发送请求到服务器以及接收服务器的响应数据逻辑控制…

三种方法用c语言求最大公约数以及最小公倍数

学习目标&#xff1a; 掌握求最大公约数&#xff08;最小公倍数&#xff09;的三种基本方法 学习内容&#xff1a; 1.一大一小取其小&#xff0c;剖根问底取公约 意思是从一大一小两个数当中&#xff0c;我们取较小的那个数&#xff08;min&#xff09;进行剖析&#xff0c;试…

Socket、UDP、TCP协议和简单实现基于UDP的客户端服务端

目录 Socket TCP和UDP区别 UDP&#xff1a;无连接&#xff0c;不可靠传输&#xff0c;面向数据报&#xff0c;全双工 TCP&#xff1a;有连接&#xff0c;可靠传输&#xff0c;面向字节流&#xff0c;全双工 无连接和有连接 可靠传输和不可靠传输 面向数据报和面向字节流…

pclpy 最小二乘法拟合平面

pclpy 最小二乘法拟合平面 一、算法原理二、代码三、结果1.左边原点云、右边最小二乘法拟合平面后点云投影 四、相关数据 一、算法原理 平面方程的一般表达式为&#xff1a; A x B y C z D 0 ( C ≠ 0 ) Ax By Cz D 0 \quad (C\neq0) AxByCzD0(C0) 即&#xff1a; …

【深度学习笔记】 3_13 丢弃法

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 3.13 丢弃法 除了前一节介绍的权重衰减以外&#xff0c;深度学习模型常常使用丢弃法&#xff08;dropout&#xff09;[1] 来应对过拟合…

HDL FPGA 学习 - Quartus II 工程搭建,ModelSim 仿真,时序分析,IP 核使用,Nios II 软核使用,更多技巧和规范总结

目录 工程搭建、仿真与时钟约束 一点技巧 ModelSim 仿真 Timing Analyzer 时钟信号约束 SignalTap II 使用 In-System Memory Content Editor 使用 记录 QII 的 IP 核使用 记录 Qsys/Nios II 相关 记录 Qsys 的 IP 核使用 封装 Avalon IP 更多小技巧教程文章 更多好…

【C语言】linux内核ipoib模块 - ipoib_tx_poll

一、中文注释 这段代码是 Linux 内核网络栈中与 InfiniBand 协议相关的一个部分&#xff0c;特别是与 IP over InfiniBand (IPoIB)相关。该函数负责去处理IPoIB的发送完成队列&#xff08;发送CQ&#xff09;上的工作请求&#xff08;work completions&#xff09;。以下是对这…

前后端分离Vue+ElementUI+nodejs蛋糕甜品商城购物网站95m4l

本文主要介绍了一种基于windows平台实现的蛋糕购物商城网站。该系统为用户找到蛋糕购物商城网站提供了更安全、更高效、更便捷的途径。本系统有二个角色&#xff1a;管理员和用户&#xff0c;要求具备以下功能&#xff1a; &#xff08;1&#xff09;用户可以修改个人信息&…

YOLO系列论文阅读(v1--v3)

搞目标检测&#xff0c;绕不开的一个框架就是yolo&#xff0c;而且更糟糕的是&#xff0c;随着yolo的发展迭代&#xff0c;yolo网络可以做的事越来越多&#xff0c;语义分割&#xff0c;关键点检测&#xff0c;3D目标检测。。。这几天决定把YOLO系列彻底梳理一下&#xff0c;在…

奇异递归模板模式应用6-类模板enable_shared_from_this

异步编程中存在一种场景&#xff0c;需要在类中将该类的对象注册到某个回调类或函数中&#xff0c;不能简单地将this传递给回调类中&#xff0c;很可能因为回调时该对象不存在而导致野指针访问&#xff08;也有可能在析构函数解注册时被回调&#xff0c;造成对象不完整&#xf…