设计模式:代理模式 - 控制访问与增强功能的艺术

news2025/4/13 4:42:06

一、为什么使用代理模式?

在开发中,你是否遇到过以下问题:

• 某些功能调用需要权限校验,但不希望修改核心逻辑?

• 某些对象的创建开销过高,希望延迟加载以优化性能?

• 在不改变原始类的情况下,如何在调用前后插入日志或监控代码?

这些场景中,代理模式提供了一种优雅的解决方案。

二、什么是代理模式?

代理模式(Proxy Pattern)属于结构型设计模式,其核心思想是提供一种代理以控制对目标对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并可以在不改变客户端代码的情况下增强或控制对目标对象的访问。从而有效提升系统的扩展性与灵活性。

代理模式的模型结构

1.抽象主题接口(Subject):定义目标对象和代理对象的公共方法。

2.真实主题(RealSubject):实现抽象主题接口,包含实际的业务逻辑。

3.代理类(Proxy):实现抽象主题接口,包含对真实主题的引用,从而操作真实主题并可提供访问控制和功能增强。

三、代理模式的主要类型及示例

代理模式主要类型

1.静态代理

特点:需要显示创建代理类,在编译期间确定代理类和目标类的绑定关系。

适用场景:功能明确、需求稳定的场景,如日志记录或权限校验等。

2.动态代理
特点:无需显示生成代理类,运行时会动态生成代理类。

适用场景:需要更高灵活性的场景

动态代理的2种实现机制:

• JDK 动态代理:通过 Java 反射机制实现,要求目标类实现接口。

• CGLib 动态代理:通过生成目标类的子类实现,无需目标类实现接口。

3.虚拟代理

特点:通过虚拟代理延迟实例化目标对象,仅在真正需要时才加载资源。 从而提高资源利用率和性能。

适用场景:对象的创建需要较大开销的场景 ,如大图片、视频加载等。

1.静态代理示例:对支付功能增加日志记录功能

第一步:定义支付接口 (目标功能)

// 定义公共接口
public interface Payment {
    void pay(double amount);
}

第二步:创建真实支付类

// 实现真实对象
public class RealPayment implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("支付金额:" + amount + "元");
    }
}

第三步:创建代理支付类

实现支付接口,增加日志功能

// 静态代理类
public class PaymentProxy implements Payment {
    private final Payment realPayment;
    public PaymentProxy(Payment realPayment) {
        this.realPayment = realPayment;
    }
    @Override
    public void pay(double amount) {
        // 在方法调用前后添加额外逻辑
        System.out.println("[代理日志] 支付前检查用户权限...");
        realPayment.pay(amount);
        System.out.println("[代理日志] 支付完成,记录支付日志。");
    }
}

第四步:客户端测试代码

通过代理对象调用真实对象的支付方法,隔离了客户端和真实对象。

// 客户端测试
public class ProxyTest {
    public static void main(String[] args) {
        Payment payment = new PaymentProxy(new RealPayment());
        payment.pay(100.0);
    }
}

输出结果:

[代理日志] 支付前检查用户权限...
支付金额:100.0元
[代理日志] 支付完成,记录支付日志。

2. 虚拟代理示例:图片延迟加载

通过虚拟代理延迟图片的创建,从而优化资源使用和性能。

第一步:定义图片接口

// 定义图片接口
public interface Image {
    void display(); // 显示图片
}

第二步:构建真实图片类

实现图片的加载和显示功能

// 真实图片类
public class RealImage implements Image {
    private String fileName;
    //构造函数
    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(); 
    }
    //模拟从磁盘加载图片
    private void loadFromDisk() {
        System.out.println("Loading " + fileName);
    }
    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }
}

第三步:构建图片代理类

实现图片接口,并对真实图片类的创建进行控制。

// 图片代理类
public class ProxyImage implements Image {

    // 持有对真实图片对象的引用
    private RealImage realImage;

    // 保存图片文件名,用于真实图片对象的创建
    private String fileName;

    // 构造函数,初始化代理类时传入图片文件名
    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    // 实现 display 方法,增加延迟加载功能
    public void display() {
        // 检查 realImage 是否为 null
        if (realImage == null) {
            // 只有realImage未创建时,才实例化。
            realImage = new RealImage(fileName);
        }
        // 调用真实图片对象的 display 方法
        realImage.display();
    }
}

第四步:测试代码,客户端调用

// 客户端调用
public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("image1.jpg");
        Image image2 = new ProxyImage("image2.jpg");
        // 第一次调用 display,会触发图片加载
        image1.display();
        System.out.println("---");
        // 第二次调用 display,不会重复加载
        image1.display();
        System.out.println("---");
        // 另一张图片首次调用,会触发图片加载
        image2.display();
    }
}

运行结果:

Loading image1.jpg  
Displaying image1.jpg  
---  
Displaying image1.jpg  
---  
Loading image2.jpg  
Displaying image2.jpg 

代码说明

1.Image 接口:定义图片的通用行为display()。

2.RealImage 类:真实图片类,实现了图片的真实加载和显示逻辑。

3.ProxyImage 类:代理图片类,控制真实图片类的加载和显示。

4.客户端代码:通过代理图片类调用真实图片类的display()方法,无需关心加载细节。

延迟加载机制:

• realImage在代理对象ProxyImage初始化时并未创建。

• ProxyImage会在首次显示图片时创建实例RealImage。

• 后续再调用display()方法时,因为真实图片类已创建,不会再次实例化加载图片。从而降低不必要的资源消耗。

如上示例代码已详细展示了如何在不同场景中应用代理模式,增强功能同时确保代码结构清晰。

3、动态代理 !!!!

动态代理通过“运行时动态生成代理类”,可帮助开发者快速解决功能增强和接口代理中的重复性问题,从而提升代码灵活性和开发效率。

注:

• AOP(面向切面编程):

Spring 的 AOP 框架,通过注解或配置实现功能增强,比动态代理更直观一些,尤其是在大型项目中。

• 模板模式:

通过定义模板方法,将重复逻辑抽象到父类中,也减少每个接口的重复实现。

动态代理模式是一种通过在运行时动态生成代理类来实现目标对象功能扩展的设计模式。它与静态代理的最大区别在于:动态代理无需手动编写代理类,代理类由框架或工具动态生成。

目前常见的动态代理实现方式有两种:

1.JDK 动态代理:基于 Java 反射机制,只能代理实现了接口的类。

2.CGLib 动态代理:基于字节码生成技术,可以代理没有实现接口的类。

JDK 动态代理是一种基于 Java 标准库java.lang.reflect包实现的代理方式,可以在运行时动态生成代理对象。代理对象实现和目标类同样的接口,并将方法调用转发给目标对象,同时可以在方法调用前后执行额外的增强处理。

生成流程:

1.创建代理类工厂

在调用Proxy.newProxyInstance方法时,动态生成代理类,该代理类会实现目标接口,并将方法调用委托给一个实现了InvocationHandler接口的实例。

2.定义 InvocationHandler 接口

InvocationHandler是 JDK 动态代理的核心接口。它只有一个invoke方法,当代理对象的方法被调用时,invoke方法会被自动触发。此方法接收以下三个参数:

• proxy:当前生成的代理对象;

• method:目标对象中被调用的方法;

• args:方法调用的参数数组。

3.调用代理对象的方法

当调用代理对象的方法时,InvocationHandler的invoke方法会被自动调用。开发者可以在invoke方法中添加增强逻辑(如日志记录、权限检查等),然后通过反射调用目标对象的方法。

4.通过反射执行目标方法

在invoke方法中,开发者可以使用反射机制调用目标对象的方法,并在调用前后插入额外的逻辑处理。最终,将目标方法的返回值返回给调用者。

JDK 动态代理通过这种机制实现了对目标方法调用的灵活控制,适合需要对实现了接口的类进行动态增强的场景。

JDK 动态代理实例

场景:在支付功能中动态添加日志。

步骤 1:定义接口

定义一个公共接口,供目标类和代理类实现。这是 JDK 动态代理的前提。

public interface Payment {
    void pay(int amount);
}

步骤 2:实现目标类

目标类实现接口,提供具体业务逻辑。

public class RealPayment implements Payment {
    @Override
    public void pay(int amount) {
        System.out.println("Processing payment of $" + amount);
    }
}

步骤 3:实现 InvocationHandler

定义一个代理处理器类,实现InvocationHandler接口,用于自定义增强逻辑。

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

public class PaymentInvocationHandler implements InvocationHandler {
    private final Payment realPayment;

    // 构造方法,接收目标对象
    public PaymentInvocationHandler(Payment realPayment) {
        this.realPayment = realPayment;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          /**
     * @param proxy  动态生成的代理对象实例,通常很少直接使用。
     * @param method 目标对象中被调用的方法。
     * @param args   方法的参数数组。
     */
  
        // 增强逻辑:方法调用前
        System.out.println("[Log] Starting payment...");

        // 调用目标方法
        Object result = method.invoke(realPayment, args);

        // 增强逻辑:方法调用后
        System.out.println("[Log] Payment finished!");

        return result;
    }
}

步骤 4:创建代理对象

通过Proxy.newProxyInstance方法生成代理对象。

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        Payment realPayment = new RealPayment();

        // 创建代理对象
        Payment proxyPayment = (Payment) Proxy.newProxyInstance(
            realPayment.getClass().getClassLoader(), // 类加载器
            realPayment.getClass().getInterfaces(),  // 目标对象实现的接口
            new PaymentInvocationHandler(realPayment) // 动态代理处理器
        );

        // 调用代理对象的方法
        proxyPayment.pay(100);
    }
}

以下是Proxy.newProxyInstance的三个参数及其含义:

1.realPayment.getClass().getClassLoader()

• 类加载器,传入目标对象的类加载器。

2.realPayment.getClass().getInterfaces()

• 目标对象实现的接口列表。

• 代理类会实现这些接口,从而与目标对象拥有相同的方法。

3.PaymentInvocationHandler(realPayment)

• 动态代理处理器,负责拦截代理对象的方法调用。

•InvocationHandler中定义了增强逻辑和目标方法的调用。

运行效果

运行上述代码,输出如下:

[Log] Starting payment...
Processing payment of $100
[Log] Payment finished!

CGLib(Code Generation Library)是一个基于 ASM(Java 字节码操作框架)实现的动态代理库。它的工作原理是,在程序运行时动态生成目标类的子类,并在子类中拦截目标方法的调用,从而实现代理功能。与 JDK 动态代理不同,CGLib 不需要目标类实现接口,而是直接通过继承目标类来创建代理类。所以可以代理没有实现接口的类。

生成流程:

1.创建Enhancer对象

CGLib 使用Enhancer类来生成代理类。通过创建Enhancer对象并设置目标类和拦截器等参数,最终生成代理类。

2.设置代理逻辑(回调拦截器)

实现MethodInterceptor接口,定义拦截逻辑。该接口的intercept方法会在每次调用代理类的方法时触发,开发者可以在intercept方法中添加增强功能,如日志记录、权限验证等。

3.动态生成代理类

CGLib 在运行时动态生成目标类的子类作为代理类。生成的代理类会继承目标类的所有方法,并在每个方法执行时触发intercept方法。

4.调用代理对象的方法

当调用代理对象的方法时,MethodInterceptor中的intercept方法会被触发,即增强逻辑(如日志记录、权限验证等)执行,目标类的实际方法通过MethodProxy.invokeSuper调用。

CGLib 动态代理实例

场景:动态代理一个没有实现接口的类,为其添加日志功能。

代码实现

步骤 1:定义目标类

目标类不需要实现接口,可以直接定义具体方法。

// 一个没有实现接口的普通类
public class UserService {
    // 模拟添加用户的操作
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }

    // 模拟删除用户的操作
    public void deleteUser(String username) {
        System.out.println("删除用户:" + username);
    }
}

步骤 2:创建 CGLib 动态代理拦截器

创建一个类实现MethodInterceptor接口,用于定义增强逻辑。

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

import java.lang.reflect.Method;

// 定义拦截器,为目标类的方法添加日志功能
public class LoggingInterceptor implements MethodInterceptor {

    /**
     * @param obj    代理对象本身(动态生成的代理类实例)
     * @param method 目标类的方法
     * @param args   目标方法的参数数组,按顺序包含每个参数值。
     * @param proxy  方法代理,用于调用目标类(父类)方法的代理工具
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */ 
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在方法调用前添加日志
        System.out.println("[日志] 调用方法:" + method.getName() + ",参数:" + java.util.Arrays.toString(args));

        // 调用目标类的原始方法
        Object result = proxy.invokeSuper(obj, args);

        // 在方法调用后添加日志
        System.out.println("[日志] 方法执行完成:" + method.getName());
        return result;
    }
}

代码解析

1.proxy.invokeSuper(obj, args):调用目标类的原始方法。

2.intercept方法参数:

步骤3:代理工厂

import net.sf.cglib.proxy.Enhancer;

// 创建代理对象的工厂类
public class CGLibProxyFactory {
    /**
     * 获取代理对象
     *
     * @param clazz 需要代理的目标类
     * @return 代理对象
     */
    public <T> T getProxy(Class<T> clazz) {
        // 创建 Enhancer 对象,用于生成代理类
        Enhancer enhancer = new Enhancer();

        // 设置目标类为代理类的父类
        enhancer.setSuperclass(clazz);

        // 设置回调拦截器,用于增强目标类的方法
        enhancer.setCallback(new LoggingInterceptor());

        // 创建代理对象
        return (T) enhancer.create();
    }
}

代码解析:

代理工厂 (CGLibProxyFactory):

• 使用 CGLib 的Enhancer创建动态代理对象。

• 设置目标类为代理类的父类,并将拦截器传递给Enhancer。

步骤4:测试代码

创建代理对象,调用目标方法时,会自动触发拦截器中的intercept方法。

public class Client {
    public static void main(String[] args) {
        // 使用代理工厂生成代理对象
        CGLibProxyFactory proxyFactory = new CGLibProxyFactory();
        UserService userServiceProxy = proxyFactory.getProxy(UserService.class);

        // 调用代理对象的方法
        userServiceProxy.addUser("Alice");
        System.out.println();
        userServiceProxy.deleteUser("Alice");
    }
}

CGLib实现的思路总结

1.定义目标类

目标类无需实现接口,直接定义业务逻辑。

2.创建拦截器

实现MethodInterceptor接口,编写增强逻辑。

3.生成代理对象

使用Enhancer设置目标类和拦截器,生成代理对象。

4.调用代理方法

代理对象的方法调用会触发拦截器,实现动态代理逻辑。

四、代理模式的价值

对开发的价值

1.单一职责:代理模式将对象的核心功能与附加功能(如延迟加载)解耦,优化代码结构。

2.扩展性强:通过代理类新增功能,无需修改核心功能类,符合开闭原则

代理模式有如下缺点:

1.增加复杂度:引入代理类,可能导致维护难度增加。

2.性能损耗:代理访问会增加额外开销。

动态代理:
动态代理的价值

1.提高代码复用性

动态代理可以统一处理多个类的公共逻辑,减少重复代码。

2.增强系统扩展性

在不修改目标类的情况下动态扩展功能,符合开闭原则。

3.简化代理类生成

通过运行时生成代理类,避免手动编写静态代理类。

4.便于模拟与隔离

动态代理可以轻松模拟接口行为,方便单元测试和集成测试。

动态代理的缺点
1.性能开销较大,依赖反射机制动态代理依赖反射机制来生成代理类、执行方法,尽管反射提供了强大的灵活性,但其性能开销较大。在高频次调用的场景下,相较于直接方法调用,动态代理的性能会有所下降,并可能引发类型安全问题。

2.增加代码复杂性,调试较困难动态代理虽然简化了代码编写,但也带来了额外的复杂性。在调试和追踪时,尤其是使用第三方库(如 Spring AOP 或 CGLib)时,代理的行为不容易直观理解,调试代理方法比直接调试目标方法更具挑战性。

3.可能导致内存泄漏如果代理对象未被正确释放,或者引用关系处理不当,可能导致内存泄漏。代理对象和目标对象之间的引用可能无法被垃圾回收器识别,导致内存无法及时释放。

五、代理模式的适用场景

1.远程代理:为远程对象提供本地代理,简化分布式系统中远程方法的调用。

2.虚拟代理:延迟加载高开销资源,优化性能。

3.安全代理:控制敏感资源的访问权限。

4.缓存代理:缓存高频访问的数据,减少重复计算或查询。

5.日志代理:自动记录操作日志。

6.增强功能代理:动态扩展事务、监控等功能。

动态代理
1.权限校验如用户角色验证、操作权限控制。

2.日志记录为方法调用添加日志,便于监控和调试。

3.事务管理在方法调用前后添加事务处理逻辑。

4.性能监控动态收集方法调用的性能数据。

5.接口适配动态创建接口实现,减少手动实现的工作量。

六、总结

代理模式的核心价值在于:通过引入代理对象,解耦目标对象与客户端,在保留系统灵活性的同时,为目标对象添加功能增强或行为控制,最终提升系统的可扩展性与稳定性。

动态代理是实现灵活功能扩展的有力工具,通过在运行时生成代理类,解决了静态代理代码冗余的问题。无论是JDK 动态代理还是CGLib 动态代理,它们都能够有效解耦客户端和目标对象,为系统提供更强的扩展性和灵活性。然而,在使用动态代理时,也需要权衡其可能带来的性能开销、代码复杂性和内存管理问题。因此,在设计时要根据具体场景谨慎选择代理方式,充分考虑这些潜在缺点。

实践建议:

• 避免代理类过多导致系统复杂度增加。

• 简单场景谨慎使用,避免过度设计。

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

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

相关文章

通过AWS EKS 生成并部署容器化应用

今天给大家分享一个实战例子&#xff0c;如何在EKS上创建容器化应用并通过ALB来发布。先介绍一下几个基本概念&#xff1a; IAM, OpenID Connect (OIDC) 2014 年&#xff0c;AWS Identity and Access Management 增加了使用 OpenID Connect (OIDC) 的联合身份支持。此功能允许…

nginx入门,部署静态资源,反向代理,负载均衡使用

Nginx在linux上部署静态资源 概念介绍 Nginx可以作为静态web服务器来部署静态资源。这里所说的静态资源是指在服务端真实存在&#xff0c;并且能够直接展示的一些文件&#xff0c;比如常见的html页面、css文件、js文件、图片、视频等资源。 相对于Tomcat&#xff0c;Nginx处理…

智膳优选 | AI赋能的智慧食堂管理专家 —— 基于飞书多维表格和扣子(Coze)的智能解决方案

智膳优选 | AI赋能的智慧食堂管理专家 基于飞书多维表格和扣子&#xff08;Coze&#xff09;的智能解决方案 数据驱动餐饮管理&#xff0c;让每一餐都是营养与经济的完美平衡&#xff01; “智膳优选”通过整合飞书与Coze&#xff0c;将数据智能引入校园餐饮管理&#xff0…

最新的es版本忘记密码,重置密码

刚刚安装了最新的es版本,就忘了密码,怎么重置密码呢? 一、进入es的斌目录 #进入es文件/bin 目录 ./elasticsearch-reset-password -u elastic 二 、输入对应的密码 然后再次访问 我的是去掉了ssl的访问 三、如果报错:解决 [main] WARN

Compose Multiplatform+Kotlin Multiplatfrom 第五弹跨平台 截图

截图功能 Compose MultiplatformKotlin Multiplatfrom下实现桌面端的截图功能&#xff0c;起码搞了两星期&#xff0c;最后终于做出来了&#xff0c;操作都很流畅&#xff0c;截取的文件大小也正常&#xff0c;可参考支持讨论&#xff01; 功能效果 代码实现 //在jvmMain下创…

Elasticearch数据流向

Elasticearch数据流向 数据流向图 --- config: layout: elk look: classic theme: mc --- flowchart LR subgraph s1["图例"] direction TB W["写入流程"] R["读取流程"] end A["Logstash Pipeline"] -- 写入请求 --> B["Elas…

在docker里装rocketmq-console

首先要到github下载&#xff08;这个一般是需要你有梯子&#xff09; GitHub - apache/rocketmq-externals at release-rocketmq-console-1.0.0 如果没有梯子&#xff0c;用下面这个百度网盘链接下 http://链接: https://pan.baidu.com/s/1x8WQVmaOBjTjss-3g01UPQ 提取码: fu…

使用Python写入JSON、XML和YAML数据到Excel文件

在当今数据驱动的技术生态中&#xff0c;JSON、XML和YAML作为主流结构化数据格式&#xff0c;因其层次化表达能力和跨平台兼容性&#xff0c;已成为系统间数据交换的通用载体。然而&#xff0c;当需要将这类半结构化数据转化为具备直观可视化、动态计算和协作共享特性的载体时&…

x-cmd install | Slumber - 告别繁琐,拥抱高效的终端 HTTP 客户端

目录 核心优势&#xff0c;一览无遗安装应用场景&#xff0c;无限可能示例告别 GUI&#xff0c;拥抱终端 还在为调试 API 接口&#xff0c;发送 HTTP 请求而苦恼吗&#xff1f;还在各种 GUI 工具之间切换&#xff0c;只为了发送一个简单的请求吗&#xff1f;现在&#xff0c;有…

apijson 快速上手

apijson是强大的工具&#xff0c;简化了CRUD的操作&#xff0c;只要有数据库表&#xff0c;就能自动生成RESTFUL接口。但初次上手也是摸索了很长时间&#xff0c;尤其是部署与使用上&#xff0c;这里尝试以初学者角度来说下&#xff1a; 一、好处 1、对于简单的应用&#xff…

3D激光轮廓仪知识整理

文章目录 1.原理和应用场景1.1 相机原理1.1.1 测量原理1.1.2 相机激光器1.1.3 沙姆镜头1.1.4 相机标定1.1.5 中心线提取 1.2 应用场景1.2.1 测量相关应用1.2.2 缺陷检测相关应用 2.相机参数介绍及选型介绍2.1 成像原理2.2 原始图成像2.3 生成轮廓图2.4 相机规格参数2.4.1 单轮廓…

Stable Diffusion+Pyqt5: 实现图像生成与管理界面(带保存 + 历史记录 + 删除功能)——我的实验记录(结尾附系统效果图)

目录 &#x1f9e0; 前言 &#x1f9fe; 我的需求 &#x1f527; 实现过程&#xff08;按功能一步步来&#xff09; &#x1f6b6;‍♂️ Step 1&#xff1a;基本图像生成界面 &#x1f5c3;️ Step 2&#xff1a;保存图片并显示历史记录 &#x1f4cf; Step 3&#xff1a…

使用WasmEdge将InternLM集成到Obsidian,打造本地智能笔记助手

本文来自社区投稿&#xff0c;作者Miley Fu&#xff0c;WasmEdge Runtime 创始成员。 本文将介绍如何通过 WasmEdge 将书生浦语&#xff08;InternLM&#xff09;大模型部署在本地&#xff0c;并与 Obsidian 笔记软件集成&#xff0c;从而在笔记软件中直接利用大模型实现文本总…

java导入excel更新设备经纬度度数或者度分秒

文章目录 一、背景介绍二、页面效果三、代码0.pom.xml1.ImportDevice.vue2.ImportDeviceError.vue3.system.js4.DeviceManageControl5.DeviceManageUserControl6.Repeater7.FileUtils8.ResponseModel9.EnumLongitudeLatitude10.词条 四、注意点本人其他相关文章链接 一、背景介…

视频设备轨迹回放平台EasyCVR远程监控体系落地筑牢国土监管防线

一、背景概述 我国土地资源遭违法滥用的现象愈发严峻&#xff0c;各类土地不合理利用问题频发。不当的土地开发不仅加剧了地质危害风险&#xff0c;导致良田受损、森林资源的滥伐&#xff0c;还引发了煤矿无序开采、城市开发区违建等乱象&#xff0c;给国家宝贵的土地资源造成…

Stable Diffusion 四重调参优化——项目学习记录

学习记录还原&#xff1a;在本次实验中&#xff0c;我基于 Stable Diffusion v1.5模型&#xff0c;通过一系列优化方法提升生成图像的质量&#xff0c;最终实现了图像质量的显著提升。实验从基础的 Img2Img 技术入手&#xff0c;逐步推进到参数微调、DreamShaper 模型和 Contro…

我可能用到的网站和软件

我可能用到的网站和软件 程序员交流的网站代码管理工具前端组件库前端框架在线工具人工智能问答工具学习的网站Windows系统电脑的常用工具 程序员交流的网站 csdn博客博客园 - 开发者的网上家园InfoQ - 软件开发及相关领域-极客邦掘金 (juejin.cn) 代码管理工具 GitHub 有时…

FPGA状态机设计:流水灯实现、Modelsim仿真、HDLBits练习

一、状态机思想 1.概念 状态机&#xff08;Finite State Machine, FSM&#xff09;是计算机科学和工程领域中的一种抽象模型&#xff0c;用于描述系统在不同状态之间的转换逻辑。其核心思想是将复杂的行为拆解为有限的状态&#xff0c;并通过事件触发状态间的转移。 2.状态机…

2024年第十五届蓝桥杯CC++大学A组--成绩统计

2024年第十五届蓝桥杯C&C大学A组--成绩统计 题目&#xff1a; 动态规划&#xff0c; 对于该题&#xff0c;考虑动态规划解法&#xff0c;先取前k个人的成绩计算其方差&#xff0c;并将成绩记录在数组中&#xff0c;记录当前均值&#xff0c;设小蓝已检查前i-1个人的成绩&…

Kotlin 学习-集合

/*** kotlin 集合* List:是一个有序列表&#xff0c;可通过索引&#xff08;下标&#xff09;访问元素。元素可以在list中出现多次、元素可重复* Set:是元素唯一的集合。一般来说 set中的元素顺序并不重要、无序集合* Map:&#xff08;字典&#xff09;是一组键值对。键是唯一的…