Java 设计模式——代理模式

news2024/11/27 16:29:07

目录

  • 1.概述
  • 2.结构
  • 3.静态代理
    • 3.1.实现
    • 3.2.优缺点
  • 4.动态代理
    • 4.1.JDK 动态代理
      • 4.1.1.实现
      • 4.1.2.思考
        • 4.1.2.1.ProxyFactory 是代理类吗?
        • 4.1.2.2.动态代理的执行流程是什么样?
        • 4.1.2.3.为什么 JDK 动态代理只能代理有接口的类?
    • 4.2.CGLIB 动态代理
  • 5.三种代理的对比
  • 6.代理模式的优缺点

1.概述

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

(2)代理模式则是一种结构型设计模式,它允许通过创建一个代理对象来控制对原始对象的访问。代理对象充当了客户端与原始对象之间的中介,并可以在访问原始对象前后执行额外的逻辑。代理模式的核心思想是通过引入一个代理对象来间接访问原始对象,从而可以在不修改原始对象的情况下增加额外的功能或控制访问

(3)代理模式提供了对对象的间接访问,使得系统更加灵活、可扩展和易于维护。它符合单一职责原则和开闭原则,并可与其他设计模式(如装饰器模式、适配器模式)相结合使用。

(4)Java 中的代理按照代理类生成时机不同又分为静态代理动态代理

  • 静态代理在编译期间就已经确定代理类和被代理类的关系
  • 动态代理代理类则是在 Java 运行时动态生成,动态代理又分为JDK 代理CGLib 代理这两种。

2.结构

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

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

3.静态代理

3.1.实现

【例】火车站卖票
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。类图如下:
在这里插入图片描述
代码如下:
SellTickets.java

//卖火车票的接口
public interface SellTickets {
    void sell();
}

TrainStation.java

//火车站类
public class TrainStation implements SellTickets{
    public void sell(){
        System.out.println("火车站买票");
    }
}

ProxyPoint.java

//代售点类
public class ProxyPoint implements SellTickets{
    
    //声明火车站类对象
    private TrainStation trainStation = new TrainStation();
    
    @Override
    public void sell() {
        System.out.println("代售点收取一些费用");
        trainStation.sell();
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        //创建代售点对象
        ProxyPoint proxyPoint = new ProxyPoint();
        //调用方法进行卖票
        proxyPoint.sell();
    }
}

从上面代码中可以看出测试类直接访问的是 ProxyPoint 类对象,也就是说 ProxyPoint 作为访问对象和目标对象的中介。同时也对 sell 方法进行了增强(代理点收取一些服务费用)。

3.2.优缺点

(1)静态代理是在编译时期创建代理类的一种代理模式。代理类在代码中显式定义,并且代理对象和目标对象之间的关系在编译时就确定了。静态代理有以下优点和缺点:

  • 优点:
    • 控制访问:静态代理可以在代理类中加入额外的逻辑来控制对目标对象的访问,例如权限验证、安全控制等。这样可以保证目标对象的安全性和一致性。
    • 增强功能:通过静态代理,可以在目标对象的方法调用前后添加额外的逻辑,实现对目标对象的功能增强,比如日志记录、性能监控、事务管理等。
    • 简单易用:相对于动态代理,静态代理的实现相对简单,不需要依赖第三方库或框架。开发人员可以更容易理解和掌控代理的行为。
    • 更好的可读性:静态代理的代码结构清晰明确,代理类与目标类的关系清晰可见,便于理解和维护。
  • 缺点:
    • 代码冗余:每个需要代理的类都需要编写一个对应的代理类,这样会导致代理类的数量增多,增加代码冗余和维护的成本。
    • 扩展性差:当目标对象的接口发生变化时,静态代理类需要相应地进行修改,违背了开闭原则。添加新的目标对象也需要创建对应的代理类,繁琐且不易扩展。
    • 代理类和目标类的紧耦合:静态代理中代理类与目标类的关系是静态确定的,代理类与目标类紧密耦合,一旦目标类发生变化,代理类也需要相应地进行调整。
    • 只能代理特定的目标对象:每个代理类只能代理指定的目标对象,不能代理其他类,限制了静态代理的复用性。

(2)总体而言,静态代理在简单的场景下使用较为方便,但在复杂的业务场景中,动态代理更加灵活和可扩展。

4.动态代理

4.1.JDK 动态代理

4.1.1.实现

下来使用 JDK 动态代理来实现上面的案例。java.lang.reflect 包中提供了一个动态代理类 Proxy,Proxy 并不是上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance 方法)来获取代理对象。实现代码如下:
SellTickets.java

package com.itheima.patterns.structuralpattern.proxy.dynamic_proxy.jdk_proxy;

//卖火车票的接口
public interface SellTickets {
    void sell();
}

TrainStation.java

package com.itheima.patterns.structuralpattern.proxy.dynamic_proxy.jdk_proxy;

//火车站类
public class TrainStation implements SellTickets {
    public void sell(){
        System.out.println("火车站卖票");
    }
}

ProxyFactory.java

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

//获取代理对象的工厂类
public class ProxyFactory {
    
    //声明目标对象
    private TrainStation station = new TrainStation();
    
    public SellTickets getProxyObject(){
        //返回代理对象
        /*
            ClassLoader loader: 类加载器,用于加载代理类。可以通过目标对象获取类加载器
            Class<?>[] interfaces: 代理类实现的接口的字节码对象
            InvocationHandler h: 代理对象的调用处理程序
         */
        SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(
                station.getClass().getClassLoader(),
                station.getClass().getInterfaces(),
                new InvocationHandler(){
                    /*
                        Object proxy: 代理对象,和 proxyObject 对象是同一个对象,在 invoke 方法中基本不用
                        Method method: 对接口中的方法进行封装的method对象
                        Object[] args: 调用方法的实际参数
                        返回值:方法的返回值。
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //System.out.println("invoke方法执行了");
                        System.out.println("代售点收取一定的服务费用(jdk动态代理)");
                        //执行目标对象的方法
                        method.invoke(station,args);
                        return null;
                    }
                }
        );
        return proxyObject;
    }
}
public class Client {
    public static void main(String[] args) {
        //获取代理对象
        //1.创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        //2.使用factory对象的方法获取代理对象
        SellTickets proxyObject = factory.getProxyObject();
        //3.调用卖票的方法
        proxyObject.sell();
    }
}

结果如下:

代售点收取一定的服务费用(jdk动态代理)
火车站买票

4.1.2.思考

4.1.2.1.ProxyFactory 是代理类吗?

ProxyFactory 不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类。这里可以通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)来查看代理类的结构,具体使用方法如下:
① 由于代理类存在于内存中,所以为了获取到该类,先让 Client.java 中的主函数一直运行,即可以在方法末尾加一个死循环(事后记得删除掉)。

//获取代理对象的运行时对象的类,结果为:class com.sun.proxy.$Proxy0
System.out.println(proxyObject.getClass());
//让主函数一直执行
while(true) {}

② 先运行 Client.java 中的主函数,然后再运行 Java 诊断工具 Arthas。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  com.itheima.patterns.structuralpattern.proxy.dynamic_proxy.jdk_proxy.SellTickets
 */
package com.sun.proxy;

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.patterns.structuralpattern.proxy.dynamic_proxy.jdk_proxy.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) 将我们提供了的匿名内部类对象传递给了父类。

4.1.2.2.动态代理的执行流程是什么样?

① 在测试类中通过代理对象调用 sell() 方法;
② 根据多态的特性,执行的是代理类 (Proxy0) 中的 sell() 方法;
③ 代理类 (Proxy0) 中的 sell() 方法中又调用了 InvocationHandler 接口的子实现类对象的 invoke 方法;
④ invoke 方法通过反射执行了真实对象所属类 (TrainStation) 中的 sell() 方法;

4.1.2.3.为什么 JDK 动态代理只能代理有接口的类?

(1)JDK 动态代理只能代理实现了接口的类,是因为 JDK 动态代理是基于接口的。在Java中,动态代理是通过反射机制来实现的。JDK 动态代理代理的是接口的方法调用,它会在运行时生成一个代理类来代替原始对象。这个代理类实现了代理的接口,并将方法的调用委托给实际的对象。

(2)由于 Java 是一种静态类型语言,并且在编译时需要明确指定类型,所以 JDK 动态代理要求被代理的类必须实现一个接口。在生成代理类时,JDK 动态代理会根据接口定义来生成代理类的代码,从而实现对接口方法的代理。

(3)如果要代理没有实现接口的类,可以使用其他的代理方式,如 CGLIB 动态代理。CGLIB 动态代理是通过生成被代理类的子类来实现代理,可以绕过接口的限制。但相对于 JDK 动态代理,CGLIB 动态代理的实现原理更加复杂,并且生成的代理类的性能也相对较低。

(4)总结来说,JDK 动态代理只能代理有接口的类,是因为 JDK 动态代理是基于接口的实现,通过生成代理类来代理接口方法的调用。如果需要代理没有实现接口的类,可以考虑使用其他的代理方式。

4.2.CGLIB 动态代理

(1)如果没有定义 SellTickets 接口,只定义了 TrainStation(火车站类)。很显然 JDK 代理是无法使用了,因为 JDK 动态代理要求必须定义接口,对接口进行代理。不过此时可以使用 CGLIB 代理来实现。CGLIB 是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。

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

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

(3)具体代码实现如下:
TrainStation.java

//火车站类
public class TrainStation{
    public void sell(){
        System.out.println("火车站卖票");
    }
}

ProxyFactory.java

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

import java.lang.reflect.Method;

//代理对象工厂,用来获取代理对象,
// Cglib 动态代理,实现 MethodInterceptor 接口
public class ProxyFactory implements MethodInterceptor {
    
    //声明火车站对象
    private TrainStation station = new TrainStation();
    
    //定义获取代理对象的方法
    public TrainStation getProxyObject(){
        //创建 Enhancer对象,类似于 JDK 代理中的 Proxy 类
        Enhancer enhancer = new Enhancer();
        //因为 Cglib 是针对指定的类生成一个子类,所以需要指定父类,即设置父类的字节码对象(指定父类)
        enhancer.setSuperclass(TrainStation.class);
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        TrainStation proxyObject = (TrainStation)enhancer.create();
        return proxyObject;
    }
    
    //重写拦截方法
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //System.out.println("方法执行了");
        System.out.println("代售点收取一定的服务费用(CGLib 代理)");
        //要调用目标对象的方法
        Object obj = method.invoke(station, args);
        return obj;
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        //创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        //获取代理对象
        TrainStation proxyObject = factory.getProxyObject();
        //调用代理对象中的 sell() 方法卖票
        proxyObject.sell();
    }
}

5.三种代理的对比

(1)JDK 代理和 CGLIB 代理
① 使用 CGLib 实现动态代理,CGLIb 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,在 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 代理

(2)动态代理和静态代理
① 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理 (InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
② 如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题

6.代理模式的优缺点

代理模式是一种常用的设计模式,它可以在不改变原始对象的前提下,为其提供额外的功能或控制访问。下面是代理模式的优点和缺点:

(1)优点:

  • 职责分离:代理模式可以将请求发送者与真实的执行者分离,在一定程度上实现了职责的分离。代理类负责处理客户端的请求和一些其他的功能,而真实对象只关注具体的业务逻辑。
  • 控制访问:代理模式可以通过代理类来控制对真实对象的访问。例如,可以在代理类中进行权限验证、安全检查或者性能监控,从而对客户端的访问进行控制。
  • 延迟加载:代理模式中的延迟加载是指在需要真实对象时才进行创建和初始化。例如,懒加载的代理可以在真正需要时才会创建对象,从而节省了系统的资源。
  • 扩展性:通过代理模式,可以通过增加代理类来扩展系统的功能,而无需修改原始类。这符合开闭原则。

(2)缺点:

  • 增加复杂性:代理模式会增加系统的复杂性,因为它引入了额外的代理类。当系统中存在大量的代理类时,代码的可读性和维护性可能会变差。
  • 性能损耗:由于代理模式通过增加代理类来实现功能的扩展和控制访问,这可能会导致一定的性能损耗。
  • 增加代码量:代理模式需要编写更多的代码,包括代理类和相关的接口、实现类等,这增加了代码量和开发工作量。

需要根据具体的场景和需求来评估代理模式的适用性,权衡其中的优缺点。在某些情况下,代理模式能够提供灵活性和可扩展性,但在其他情况下,它可能会带来不必要的复杂性和性能开销。

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

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

相关文章

初识mysql数据库之数据类型

目录 一、数据类型 1. 数据类型分类 2. 数值类型 2.1 整数类型 2.2 位字段类型 2.3 小数类型 3. 字符串类型 3.1 char 3.2 varchar 4. 日期和时间类型 5. enum和set 5.1 enum介绍 5.2 set介绍 5.3 enum测试 5.4 set测试 5.5 enum的查找 5.6 set的查找 一、数据…

DAY40:贪心算法(九)单调递增的数字(贪心的思路)

文章目录 738.单调递增的数字&#xff08;暴力解也需要看一下&#xff09;暴力解写法注意&#xff1a;必须引入isIncreasing变量的原因 贪心思路遍历顺序 最开始的写法debug测试&#xff1a;逻辑错误 修改版debug测试int转化为字符串的原因to_string和stoi的用法 总结 738.单调…

CPCI-QU-216A正交解码计数器卡

CPCI-QU-216A是用于高速正交解码计数器CPCI板卡&#xff0c;包括6个立计数器&#xff0c;支持索引锁存&#xff0c;支持步长比较输出&#xff0c; 6个高速隔离输入&#xff0c;6个高速隔离输出。 l 32位 cPCI接口 l 6通道隔离输入 l 6通道隔离输出&#xff08;默认用于比较值输…

0125 计算机系统概述

目录 1.计算机系统概述 1.1计算机发展历程 1.2计算机系统层次结构 计算机硬件 计算机软件 计算机系统层次结构 计算机系统工作原理 指令执行过程 1.2部分习题 1.3计算机性能指标 计算机主要性能指标 几个专业术语 1.3部分习题 1.计算机系统概述 1.1计算机发展历程…

三星面板产能紧缺?超半数三星电视机采用其他厂商的面板

今年有超过50%的三星电视未使用自家屏幕&#xff0c;而是采用了来自中国厂商的大部分零件。 据韩国媒体《The Elec》报道&#xff0c;超过一半的三星电视机使用了其他厂商的面板。根据三星电子面板库存明细&#xff0c;大部分的面板来自中国的华星光电、惠科、京东方和咸阳彩虹…

小红书数据分析!Citywalk声量大涨,年轻人为何迷恋它?

近来&#xff0c;一种新型旅游形式——citywalk火了。惬意的城市漫游&#xff0c;成为时下年轻人最潮的逛GAI方式。小红书上更是掀起了citywalk游记潮流&#xff0c;人们纷纷在平台分享记录自己的出游感受。citywalk具体怎么玩&#xff1f;哪些人爱玩&#xff1f;通过分析小红书…

我们来谈谈tcp

"让新离开地表&#xff0c;才能找到盘旋爬升的动力。" 一、认识Tcp报头 (1) 协议报头格式 我们先来认识认识tcp协议报头字段。 跟tcp协议字段报头比起来&#xff0c;udp可真是太轻松了。 协议字段作用源/目的端口号从哪里来&#xff0c;到哪里去32位序号/32位确认…

前端vue入门(纯代码)21_vuex

努力不一定成功&#xff0c;但是&#xff0c;不努力一定很轻松&#xff01;&#xff01;&#xff01; 【23.Vuex】 [可以去官网看看Vuex3文档](Vuex 是什么&#xff1f; | Vuex (vuejs.org)) 问题1&#xff1a;Vuex是什么&#xff1f; 【官方理解1】&#xff1a;Vuex 是一个专…

vue中的.env全局配置

关于文件名&#xff1a; .env 全局默认配置文件&#xff0c;不论什么环境都会加载合并 .env.development 开发环境下的配置文件 .env.production 生产环境下的配置文件 .env文件配置&#xff1a; 我这里要讲的env配置是用启动命令启动项目来配置不同的全局变量 我配置了两…

十七、Jenkins(centos7系统)运行python3代码

十七、Jenkins(centos7系统)运行python3代码 source /usr/python/envs/everyday/bin/activate #激活python3 虚拟环境 创建虚拟环境&#xff1a;https://blog.csdn.net/qq_42846555/article/details/131579627 source /usr/python/envs/everyday/bin/activate #激活python3 虚…

自我介绍,千万别来虚的!

大家好&#xff0c;我是鲏。 已经帮小伙伴改了 500 多份简历了&#xff0c;也发现了一些大家写简历时的共性问题。其中让我印象比较深刻的一个点就是 自我介绍 &#xff0c;基本上所有同学的自我介绍都是这么写的&#xff1a; 读这篇文章的朋友们&#xff0c;你是不是也是这么…

困于“耐用焦虑”的绿源,还在歧路徘徊?

老牌两轮电动车品牌绿源上市之旅“多歧路”。 日前&#xff0c;北京市市场监督管理局公布北京市电动自行车产品质量监督抽查结果&#xff0c;绿源两款电动自行车因存在问题被点名&#xff0c;充电器和蓄电池、整车质量、控制系统等不符合标准。 而在此前&#xff0c;绿源还向港…

秒懂算法 | 围棋中的Alpha-Beta剪枝算法

01、Alpha-Beta剪枝算法 极小化极大算法会遍历所有的可能性&#xff0c;但是根据经验可以知道&#xff0c;并不是所有的选项都需要进行深入的考虑&#xff0c;存在着某些明显不利的选项&#xff0c;当出现这种选项时就可以换一种思路进行考虑了。Alpha-Beta剪枝算法的出现正是…

网络投票平台发起投票平台投票吧网络投票平台

小程序投票活动如何做&#xff1f;很多企业在运营当中&#xff0c;都会通过投票活动来进行推广&#xff0c;从而达到吸粉、增加用户粘度等效果。而此类投票活动&#xff0c;通过小程序就可以实现&#xff0c;操作简单。 我们现在要以“青春大不同”为主题进行一次投票活动&…

系统架构设计师-软件工程(3)

一、软件系统建模 1、结构化建模方法 结构化建模方法是以过程为中心的技术&#xff0c;可用于分析一个现有系统以及定义新系统的业务需求。结构化建模方法所绘制的模型称为数据流图&#xff08;DFD&#xff09;。对于流程较为稳定的系统可考虑结构化建模方法。 2、信息工程建模…

linux中的目录文件都是用来做什么的

1、linux目录系列 - /bin、/sbin目录 我们平时使用的一些命令&#xff0c;是以2进制的格式存放在bin目录下面。例如:cat、chmod、chown、cp、date、find、gzip、kill、ln、ls、mount、mv、ping、pwd、rm、su、tar、vi等。/sbin下存放的是超级用户权限的系统指令。主要放置一些系…

Python采集双色球历史开奖信息,看看哪个号中奖概率更大

目录标题 前言知识点:开发环境:基本流程:代码展示尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 知识点: 爬虫基本流程 requests的使用 动态数据抓包 开发环境: 解释器: python 3.8 编辑器: pycharm 2022.3 requests >>> pip install requests 第三…

Jenkins邮件配置报错com.sun.mail.smtp.SMTPSenderFailedException: 501

Jenkins邮件配置&#xff0c;配置完成各种信息之后&#xff0c;“通过发送测试邮件测试配置”点击Test configuration&#xff0c;报错 1、报错信息 com.sun.mail.smtp.SMTPSenderFailedException: 501 mail from address must be same as authorization userat com.sun.mail…

ARM_异常处理流程_编写软中断swi验证保存现场和恢复现场

keil .text .global _start _start:1.构建异常向量表b resetb undefb software_interruptb prefetch_abortb data_abortb .b irqb fiq reset:系统上电之后处于svc模式初始化svc模式下的栈指针ldr sp,0x400008002.从SVC模式切换到user模式 msr cpsr,#0xD0mrs r0,cpsrorr r0,r0,…

C#(五十四)之线程Mutex互斥

Mutex&#xff08;互斥体&#xff09;&#xff1a; 排他性的使用共享资源称为线程间的互斥。 使用Mutex类要比使用monitor类消耗更多的系统资源&#xff0c;但他可以跨越多个应用程序&#xff0c;在多个应用程序间同步。 构造函数 Mutex() 使用默认属性初始化 Mutex 类的新…