文章目录

    • 1.静态代理
    • 2.动态代理
    • 3.实现简单的Retrofit
      • 定义对应的请求注解参数
      • 通过动态代理模拟Retrofit的创建
      • 请求参数的处理
      • 定义请求接口
      • 测试请求

1.静态代理

代理默认给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗来讲,代理模式就是我们生活中常见的中介。比如你按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然后真正做事情的可能就是另一个小姐姐了。

目的:

  1. 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
  2. 通过代理对象来访问控制;

在这里插入图片描述
代理模式一般会有三个角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。

比如:抽象角色
创建一个接口

public interface Image {void display();
}

代理角色:

public class ProxyImage implements Image{private RealImage realImage;private String fileName;public ProxyImage(String fileName){this.fileName = fileName;}@Overridepublic void display() {if(realImage == null){realImage = new RealImage(fileName);}realImage.display();}
}

真实角色:

public class RealImage implements Image {private String fileName;public RealImage(String fileName){this.fileName = fileName;loadFromDisk(fileName);}@Overridepublic void display() {System.out.println("Displaying " + fileName);}private void loadFromDisk(String fileName){System.out.println("Loading " + fileName);}
}

当被请求的时候,则使用代理类获取来获取真实对象

public class ProxyPatternDemo {public static void main(String[] args) {Image image = new ProxyImage("test_10mb.jpg");// 图像将从磁盘加载image.display(); System.out.println("");// 图像不需要从磁盘加载image.display();  }
}

打印:

Loading test_10mb.jpg
Displaying test_10mb.jpgDisplaying test_10mb.jpg

优点:

  • 可以在不修改目标对象的前提下,增加功能
  • 保护目标对象,客户端不直接访问真实对象

缺点:
一个代理类只能代理一个接口/类,代码冗余严重
比如你还要代理一个其他类,就要再写一个对应的代理类,重复代码

举个例子来对比:

静态代理:新增接口 ->必须改代码,写新代理类
动态代理:新增接口 ->完全不用改代码,直接支持

假设有一个服务平台,现在有两类服务:
抽象角色类:

// 发送短信
public interface SmsService {void sendSms(String msg);
}// 发送邮件
public interface EmailService {void sendEmail(String to, String subject);
}

真实实现类:

public class SmsServiceImpl implements SmsService {@Overridepublic void sendSms(String msg) {System.out.println("发送短信: " + msg);}
}public class EmailServiceImpl implements EmailService {@Overridepublic void sendEmail(String to, String subject) {System.out.println("发送邮件到 " + to + ",主题: " + subject);}
}

静态代理的痛苦,每新增一个接口就要写一个代理类
起初只有SmsService,写了一个代理

public class SmsServiceProxy implements SmsService {private final SmsService smsService;public SmsServiceProxy(SmsService smsService) {this.smsService = smsService;}@Overridepublic void sendSms(String msg) {System.out.println("【日志】准备发送短信...");smsService.sendSms(msg);System.out.println("【日志】短信发送完成!");}
}

现在要加EmailService,必须再写一个代理类

public class EmailServiceProxy implements EmailService {private final EmailService emailService;public EmailServiceProxy(EmailService emailService) {this.emailService = emailService;}@Overridepublic void sendEmail(String to, String subject) {System.out.println("【日志】准备发送邮件...");emailService.sendEmail(to, subject);System.out.println("【日志】邮件发送完成!");}
}

你会发现:两个代理类的逻辑一模一样,只是接口不同,但你必须要写两个类,否则无法代理。
如果再加WeChatService、PushService…则需要写WeChatServiceProxy、PushServiceProxy…

当然,也可以代理类和实现类同时实现两个接口

// 手动实现两个接口
public class NotificationProxy implements SmsService, EmailService {private final NotificationServiceImpl target;public NotificationProxy(NotificationServiceImpl target) {this.target = target;}@Overridepublic void sendSms(String msg) {System.out.println("【静态代理】准备发短信...");target.sendSms(msg);System.out.println("【静态代理】短信发送完成");}@Overridepublic void sendEmail(String to, String subject) {System.out.println("【静态代理】准备发邮件...");target.sendEmail(to, subject);System.out.println("【静态代理】邮件发送完成");}
}
public class NotificationServiceImpl implements SmsService, EmailService {@Overridepublic void sendSms(String msg) {System.out.println("发送短信: " + msg);}@Overridepublic void sendEmail(String to, String subject) {System.out.println("发送邮件到: " + to + ",主题: " + subject);}
}
NotificationProxy proxy = new NotificationProxy(new NotificationServiceImpl());SmsService smsProxy = proxy;
smsProxy.sendSms("你好");EmailService emailProxy = proxy;
emailProxy.sendEmail("user@abc.com", "测试");

但这样的话,代码重复,维护困难
每个方法都要手动写一遍代码逻辑
如果新增WeChatService接口,你必须

  • 修改NotificationProxy类
  • 添加implements WeChatService
  • 实现sendWeChat()方法

2.动态代理

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期间动态创建一个Class。JDK提供了Proxy来完成这件事情。基本使用如下:
比如动态代理可以同时代理两个接口

NotificationServiceImpl target = new NotificationServiceImpl();// 创建代理,让它同时实现两个接口
Object proxy = Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{SmsService.class, EmailService.class},  // 同时代理两个接口new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("【日志】调用方法: " + method.getName() + " 开始...");Object result = method.invoke(target, args);System.out.println("【日志】调用方法: " + method.getName() + " 结束.");return result;}}
);
// 当作 SmsService 使用
SmsService smsProxy = (SmsService) proxy;
smsProxy.sendSms("你好啊");// 当作 EmailService 使用
EmailService emailProxy = (EmailService) proxy;
emailProxy.sendEmail("user@abc.com", "订单确认");

输出结果

【日志】调用方法: sendSms 开始...
发送短信: 你好啊
【日志】调用方法: sendSms 结束.【日志】调用方法: sendEmail 开始...
发送邮件到: user@abc.com,主题: 订单确认
【日志】调用方法: sendEmail 结束.

动态代理也可以需要一个通用的处理器,就能代理任意接口

public class LogInvocationHandler implements InvocationHandler {private final Object target;//真实对象public LogInvocationHandler(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("日志调用方法:"+method.getName()+"开始...");Object result = method.invoke(target,args);//调用真实方法System.out.println("日志调用方法:"+method.getName()+"结束...");return result;}
}
public class Test {public static void main(String[] args) {//代理SmsServiceSmsService sms = new SmsServiceImpl();SmsService smProxy = (SmsService) Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{SmsService.class},new LogInvocationHandler(sms));smProxy.sendSms("你好啊");//代理EmailServiceEmailService email = new EmailServiceImpl();EmailService emailProxy = (EmailService)Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{EmailService.class},new LogInvocationHandler(email));emailProxy.sendEmail("user@abc.com","订单确认");}
}

输出结果

日志调用方法:sendSms开始...
发送短信:你好啊
日志调用方法:sendSms结束...
日志调用方法:sendEmail开始...
发送邮件到 user@abc.com,主题:订单确认
日志调用方法:sendEmail结束...

注意要避免的问题

public class LogInvocationHandler implements InvocationHandler {private final Object target;//真实对象public LogInvocationHandler(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(proxy);System.out.println("日志调用方法:"+method.getName()+"开始...");Object result = method.invoke(target,args);//调用真实方法System.out.println("日志调用方法:"+method.getName()+"结束...");return result;}
}

如果你在动态代理的InvocationHandler 里的invoke方法打印一下proxy,你会发现报了栈溢出的错
在这里插入图片描述
因为你打印proxy其实默认会走proxy的toString()方法也会回到到invoke方法造成栈溢出
我们也可以看一下Retrofit的代码,可以看到当使用到Object的方法的时候,传递的是this,而不是proxy代理对象。如果你写 method.invoke(proxy, args),可能会再次触发 invoke(),导致无限递归!所以 Retrofit 选择调用 this,避免循环。
在这里插入图片描述
在生成Class文件的时候,有两种方式一种是.java源文件通过javac编译生成的.class文件,再通过类加载生成Class对象,这种方式生成的class文件来源是在硬盘。而动态代理生成的class文件来源在内存生成的。
动态代理生成的源代码

 @CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();// Android-removed: SecurityManager calls/*final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}*//** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {// Android-removed: SecurityManager / permission checks./*if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}*/final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {// BEGIN Android-removed: Excluded AccessController.doPrivileged call./*AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});*/cons.setAccessible(true);// END Android-removed: Excluded AccessController.doPrivileged call.}return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}
Class<?> cl = getProxyClass0(loader, interfaces); ↓
JVM 调用 ProxyGenerator 生成字节码↓
ProxyGenerator.generateProxyClass(...) 返回 byte[],生成的Class数据的byte数组↓
JVM defineClass(byte[]) 加载这个类,将这个class数据转成一个Class对象

验证

        String name = SmsService.class.getName()+"$proxy0";//两个参数,生成的类名,代理的接口byte[] bytes = ProxyGenerator.generateProxyClass(name,new Class[]{SmsService.class});FileOutputStream fos = new FileOutputStream("src/"+name+".class");fos.write(bytes);fos.close();

在这里插入图片描述

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package agent;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class SmsService$proxy0 extends Proxy implements SmsService {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public SmsService$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 void sendSms(String var1) throws  {try {super.h.invoke(this, m3, 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 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"));m3 = Class.forName("agent.SmsService").getMethod("sendSms", Class.forName("java.lang.String"));m2 = Class.forName("java.lang.Object").getMethod("toString");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

所以我们使用动态代理,Proxy.newProxyInstance代理SmsService接口生成的.class文件就是上面的样子

SmsService sms = new SmsServiceImpl();SmsService smProxy = (SmsService) Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{SmsService.class},new LogInvocationHandler(sms));smProxy.sendSms("你好啊");smProxy.toString();

这里可以看到SmsService$proxy0类里的构造方法可以看到super(var1);会将new LogInvocationHandler(sms)传递进去。
super(var1)进入Proxy.java类的构造方法

    protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}

可以看到将当前的this.h=h,赋值给当前类的InvocationHandler
而当调用smProxy.sendSms(“你好啊”);的时候就会调用SmsService$proxy0类里sendSms方法

    public final void sendSms(String var1) throws  {try {super.h.invoke(this, m3, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}

这个时候调用super.h.invoke(this, m3, new Object[]{var1}),就是将对应的信息回调给LogInvocationHandler里的invoke方法。
this就是当前生成的代理类对象。
m3可以看到Class.forName(“agent.SmsService”).getMethod(“sendSms”, Class.forName(“java.lang.String”))
通过反射的方式生成对应的method对象。

3.实现简单的Retrofit

定义对应的请求注解参数

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {String value() default "";
}
@Target(ElementType.METHOD)//作用在方法上
@Retention(RetentionPolicy.RUNTIME) //保留期在运行期间
public @interface POST {String value() default "";
}
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {String value();
}

通过动态代理模拟Retrofit的创建

/*** 通过动态代理模拟Retrofit的创建*/
public class MyRetrofit {final Map<Method,ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();final Call.Factory callFactory;final HttpUrl baseUrl;MyRetrofit(Call.Factory callFactory,HttpUrl baseUrl){this.callFactory = callFactory;this.baseUrl = baseUrl;}public <T> T create(final Class<T> service){return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//解析这个method上所有注解ServiceMethod serviceMethod = loadServiceMethod(method);//args就是传递参数的值return serviceMethod.invoke(args);}});}private ServiceMethod loadServiceMethod(Method method) {//先不上锁,避免synchronized的性能损失ServiceMethod result = serviceMethodCache.get(method);if(result!=null) return result;//多线程下,避免重复解析synchronized (serviceMethodCache){result = serviceMethodCache.get(method);if(result==null){result = new ServiceMethod.Builder(this,method).build();serviceMethodCache.put(method,result);}}return result;}/*** 构建者模式,将一个复杂对象的构建和它的表示分离,可以使用者不必知道内部组成的细节*/public static final class Builder{private HttpUrl baseUrl;private Call.Factory callFactory;public Builder callFactory(Call.Factory factory){this.callFactory = factory;return this;}public Builder baseUrl(String baseUrl){this.baseUrl = HttpUrl.get(baseUrl);return this;}public MyRetrofit build() throws IllegalAccessException {if(baseUrl == null){throw new IllegalAccessException("Base URL required");}Call.Factory callFactory = this.callFactory;if(callFactory == null){callFactory = new OkHttpClient();}return new MyRetrofit(callFactory,baseUrl);}}}

请求参数的处理

public class ServiceMethod {private final Call.Factory callFactory;private final String relativeUrl;private final boolean hasBody;private final ParameterHandler[] parameterHandler;HttpUrl baseUrl;String httpMethod;private FormBody.Builder formBuild;HttpUrl.Builder urlBuilder;public ServiceMethod(Builder builder) {baseUrl = builder.myRetrofit.baseUrl;callFactory = builder.myRetrofit.callFactory;httpMethod = builder.httpMethod;relativeUrl = builder.relativeUrl;hasBody = builder.hasBody;parameterHandler = builder.parameterHandler;//如果是由请求体,创建一个okhttp的请求体对象if(hasBody){formBuild = new FormBody.Builder();}}public Object invoke(Object[] args) {/*** 处理请求的地址与参数* parameterHandler存的key的顺序和args参数的值顺序一一对应*/for (int i = 0; i < parameterHandler.length; i++) {ParameterHandler handlers = parameterHandler[i];//handler内本来就记录了key,现在给到对应的valuehandlers.apply(this,args[i].toString());}//获取最终请求地址HttpUrl url;if(urlBuilder == null){urlBuilder = baseUrl.newBuilder(relativeUrl);}url = urlBuilder.build();//请求体FormBody formBody = null;if(formBuild!=null){formBody = formBuild.build();}Request request = new Request.Builder().url(url).method(httpMethod,formBody).build();return callFactory.newCall(request);}//get请求,把k-v拼到url里面public void addQueryParameter(String key, String value) {if(urlBuilder == null){urlBuilder = baseUrl.newBuilder(relativeUrl);//将baseUrl和relativeUrl拼到一起}//在url后面加k-vurlBuilder.addQueryParameter(key,value);}//post请求,把k-v放到请求体中public void addFiledParameter(String key, String value) {formBuild.add(key,value);}public static class Builder{private final MyRetrofit myRetrofit;private final Annotation[] methodAnnotations;private final Annotation[][] parameterAnnoations;private String httpMethod;private String relativeUrl;private boolean hasBody;private ParameterHandler[] parameterHandler;public Builder(MyRetrofit myRetrofit, Method method){this.myRetrofit = myRetrofit;//获取方法上的所有的注解methodAnnotations = method.getAnnotations();//获得方法参数的所有的注解(一个参数可以有多个注解,一个方法又会有多个参数)parameterAnnoations = method.getParameterAnnotations();}public ServiceMethod build(){/*** 解析方法上的注解,只处理POST和GET*/for (Annotation methodAnnotation : methodAnnotations) {if(methodAnnotation instanceof POST){//记录当前请求方式this.httpMethod = "POST";//记录请求url的paththis.relativeUrl = ((POST) methodAnnotation).value();//是否有请求体this.hasBody = true;}else if(methodAnnotation instanceof GET){this.httpMethod = "GET";this.relativeUrl = ((GET) methodAnnotation).value();this.hasBody = false;}}/*** 解析方法参数的注解*/int length = parameterAnnoations.length;parameterHandler = new ParameterHandler[length];for(int i =0;i<length;i++){//一个参数上的所有注解Annotation[] annotations = parameterAnnoations[i];//处理参数上的每一个注解for (Annotation annotation : annotations) {//可以加一个判断,如果httpMethod是get请求,现在又解析到Filed注解,可以提示使用者使用Query注解if(annotation instanceof Field){String value = ((Field) annotation).value();parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);//等于在一个新的类中记录key}else if(annotation instanceof Query){String value = ((Query) annotation).value();parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);}}}return new ServiceMethod(this);}}}
public abstract class ParameterHandler {abstract void apply(ServiceMethod serviceMethod,String value);static class QueryParameterHandler extends ParameterHandler{String key;public QueryParameterHandler(String key){this.key = key;}@Overridevoid apply(ServiceMethod serviceMethod, String value) {serviceMethod.addQueryParameter(key,value);}}static class FiledParameterHandler extends ParameterHandler{String key;public FiledParameterHandler(String key){this.key = key;}@Overridevoid apply(ServiceMethod serviceMethod, String value) {serviceMethod.addFiledParameter(key,value);}}}

定义请求接口

//https://www.wanandroid.com/article/list/0/json?cid=60
public interface MyTestApi {@GET("/article/list/0/json")Call getTestApi(@Query("cid") String cid);/*@GET("/project/list/1/json")Call getWeather(@Query("cid") String cid);
*/@POST("/user/login")Call postTestApi(@Field("username") String username, @Field("password") String password);}

测试请求

class MainActivity3 : AppCompatActivity() {@SuppressLint("MissingInflatedId", "SetTextI18n")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main3)val myRetrofit = MyRetrofit.Builder().baseUrl("https://www.wanandroid.com").build()val weatherApi = myRetrofit.create(MyTestApi::class.java)val call = weatherApi.getTestApi("60")call.enqueue(object :  Callback {override fun onFailure(call: Call, e: IOException) {Log.d("cyr", "onFailure: $e")}override fun onResponse(call: Call, response: Response) {Log.d("cyr","onResponse get:"+response.body())}})val postCall = weatherApi.postTestApi("xxxx","xxxx")postCall.enqueue(object : Callback{override fun onFailure(call: Call, e: IOException) {}override fun onResponse(call: Call, response: Response) {val body = response.body()try {val string = body!!.string()Log.i("cyr", "onResponse post: $string")} catch (e: IOException) {e.printStackTrace()} finally {body!!.close()}}})}}

在这里插入图片描述
可以看到输出了请求成功结果日志。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matter安全实现

Matter分析与安全验证 上一篇文章简单的介绍了Matter的架构、实现、以及部分安全验证过程&#xff1b;这里继续补充一下Matter的其他安全验证流程&#xff0c;以更好的实现Matter安全。 Matter提供的安全实现流程大概总结起来是这个流程 硬件信任根→安全启动→动态证书→加密…

从基础到实践:Web核心概念与Nginx入门全解析

从基础到实践&#xff1a;Web核心概念与Nginx入门全解析 文章目录从基础到实践&#xff1a;Web核心概念与Nginx入门全解析一、Web是什么&#xff1f;从基本概念到核心架构1.1 Web的本质&#xff1a;一个超文本信息系统1.2 B/S架构&#xff1a;Web的“前端-后端”分工模式二、一…

【完整源码+数据集+部署教程】加工操作安全手套与手部检测系统源码和数据集:改进yolo11-cls

背景意义 研究背景与意义 随着工业自动化和智能制造的迅速发展&#xff0c;工人安全问题日益受到重视。特别是在涉及重型机械和危险操作的工作环境中&#xff0c;工人手部的安全保护显得尤为重要。传统的安全手套虽然在一定程度上能够保护工人的手部&#xff0c;但在复杂的加工…

代码随想录算法训练营第一天 || (双指针)27.移除元素 26.删除有序数组中的重复项 283.移动零 977.有序数组的平方

代码随想录算法训练营第一天 || (双指针)27.移除元素 26.删除有序数组中的重复项 283.移动零 27.移除元素 暴力方法 同向双指针双指针 自己AC的解答 卡哥的讲解 26.删除有序数组中的重复项 同向双指针 283.移动零 自己解答 灵神做法(同向双指针+交换) 977.有序数组的平方 暴…

Java全栈开发工程师面试实录:从基础到实战的深度探讨

Java全栈开发工程师面试实录&#xff1a;从基础到实战的深度探讨 一、初识与自我介绍 面试官&#xff08;李工&#xff09;&#xff1a; 你好&#xff0c;欢迎来到我们公司。我是负责技术面试的李工&#xff0c;今天我们将进行一场关于Java全栈开发的深入交流。你可以先简单介绍…

Kafka:Java开发的消息神器,你真的懂了吗?

Kafka&#xff1a;Java开发的消息神器&#xff0c;你真的懂了吗&#xff1f; 一、Kafka 是什么鬼&#xff1f; 想象一下&#xff0c;你在网上疯狂剁手后&#xff0c;满心期待着快递包裹的到来。这时候&#xff0c;快递站就像是 Kafka&#xff0c;而你的包裹就是消息。快递站接…

深度学习之第八课迁移学习(残差网络ResNet)

目录 简介 一、迁移学习 1.什么是迁移学习 2. 迁移学习的步骤 二、残差网络ResNet 1.了解ResNet 2.ResNet网络---残差结构 三、代码分析 1. 导入必要的库 2. 模型准备&#xff08;迁移学习&#xff09; 3. 数据预处理 4. 自定义数据集类 5. 数据加载器 6. 设备配置…

Pinia 两种写法全解析:Options Store vs Setup Store(含实践与场景对比)

目标&#xff1a;把 Pinia 的两种写法讲透&#xff0c;写明“怎么写、怎么用、怎么选、各自优缺点与典型场景”。全文配完整代码与注意事项&#xff0c;可直接当团队规范参考。一、背景与准备 适用版本&#xff1a;Vue 3 Pinia 2.x安装与初始化&#xff1a; # 安装 npm i pini…

setup函数相关【3】

目录1.setup函数&#xff1a;1.概述&#xff1a;2.案例分析&#xff1a;2.setup函数的优化&#xff1a;&#xff08;setup语法糖&#xff09;优化1&#xff1a;优化2&#xff1a;安装插件&#xff1a;安装指令&#xff1a;只对当前项目安装配置vite.config.ts&#xff1a;代码编…

如何通过AI进行数据资产梳理

最终产出 数据资产清单 包含所有数据资产的详细目录,列出数据集名称、描述、所有者、格式、存储位置和元数据。 用途:帮助政府部门清晰了解数据资产分布和状态。 数据质量报告 数据质量评估结果,记录准确性、完整性、一致性等问题及改进建议,基于政府认可的数据质量框架(如…

【传奇开心果系列】Flet框架结合pillow实现的英文文字倒映特效自定义模板特色和实现原理深度解析

Flet框架结合pillow实现的英文文字倒映特效自定义模板特色和实现原理深度解析 一、效果展示截图 二、使用场景 三、特色说明 四、概括说明 五、依赖文件列表 六、安装依赖命令 七、 项目结构建议 八、注意事项 九、Flet 文字倒影效果实现原理分析 (一)组件结构与功能 1. 图像…

2025最新深度学习面试必问100题--理论+框架+原理+实践 (下篇)

2025最新深度学习面试必问100题–理论框架原理实践 (下篇) 在上篇中&#xff0c;我们已经深入探讨了机器学习基础、CNN、RNN及其变体&#xff0c;以及模型优化的核心技巧。 在下篇中&#xff0c;我们将把目光投向更远方&#xff0c;聚焦于当今AI领域最炙手可热的前沿。我们将深…

原子工程用AC6编译不过问题

…\Output\atk_h750.axf: Error: L6636E: Pre-processor step failed for ‘…\User\SCRIPT\qspi_code.scf.scf’修改前&#xff1a; #! armcc -E ;#! armclang -E --targetarm-arm-none-eabi -mcpucortex-m7 -xc /* 使用说明 ! armclang -E --targetarm-arm-none-eabi -mcpuco…

Python有哪些经典的常用库?(第一期)

目录 1、NumPy (数值计算基础库) 核心特点&#xff1a; 应用场景&#xff1a; 代码示例&#xff1a; 2、Pandas (数据分析处理库) 应用场景&#xff1a; 代码示例&#xff1a; 3、Scikit-learn (机器学习库) 核心特点&#xff1a; 应用场景&#xff1a; 代码示例&am…

现代 C++ 高性能程序驱动器架构

&#x1f9e0; 现代 C 高性能程序驱动器架构M/PA&#xff08;多进程&#xff09;是隔离的“孤岛”&#xff0c;M/TA&#xff08;多线程&#xff09;是共享的“战场”&#xff0c;EDSM&#xff08;事件驱动&#xff09;是高效的“反应堆”&#xff0c;MDSM&#xff08;消息驱动&…

投资储能项目能赚多少钱?小程序帮你测算

为解决电网负荷平衡、提升新能源消纳等问题&#xff0c;储能项目的投资开发越来越多。那么&#xff0c;投资储能项目到底能赚多少钱&#xff1f;适不适合投资&#xff1f;用“绿虫零碳助手”3秒钟精准测算。操作只需四步&#xff0c;简单易懂&#xff1a;1.快速登录&#xff1a…

Mac 能够连Wife,但是不能上网问题解决

请按照以下步骤从最简单、最可能的原因开始尝试&#xff1a; 第一步&#xff1a;基础快速排查 这些步骤能解决大部分临时性的小故障。 重启设备&#xff1a;关闭您的 Mac 和路由器&#xff0c;等待一分钟后再重新打开。这是解决网络问题最有效的“万能药”。检查其他设备&am…

基于SpringBoot的旅游管理系统的设计与实现(代码+数据库+LW)

摘要 本文阐述了一款基于SpringBoot框架的旅游管理系统设计与实现。该系统整合了用户信息管理、旅游资源展示、订单处理流程及安全保障机制等核心功能&#xff0c;专为提升旅游行业的服务质量和运营效率而设计。 系统采用前后端分离架构&#xff0c;前端界面设计注重跨设备兼…

Springboot乐家流浪猫管理系统16lxw(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表项目功能&#xff1a;领养人,流浪猫,领养申请开题报告内容基于Spring Boot的乐家流浪猫管理系统开题报告一、研究背景与意义随着城市化进程加速和人口增长&#xff0c;流浪猫问题已成为全球性社会挑战。据统计&#xff0c;全球每年约有1.5亿只无家可归的宠物&a…

函数定义跳转之代码跳转

相信大家在开发的过程中都有用到函数定义跳转的功能&#xff0c;在 IDE 中&#xff0c;如果在函数调用的地方停留光标&#xff0c;可能会提示对应的函数定义&#xff0c;在 GitHub 中也是如此&#xff0c;对于一些仓库来说&#xff0c;我们可以直接查看对应的函数定义了&#x…