当前位置: 首页 > article >正文

Android-OkHttp与Retrofit学习总结

OkHttp核心机制与工作流程

面试官​:能简单介绍一下OkHttp的工作流程吗?
候选人​:
好的,OkHttp的工作流程大致可以分为几个步骤。首先,我们需要创建一个OkHttpClient实例,通常会用建造者模式来配置参数,比如设置连接超时、读取超时,或者添加全局的拦截器。这一步相当于准备好了一个“网络请求工具箱”。

接下来,我们会用Request.Builder来构建具体的请求对象Request,设置URL、请求方法(比如GET或POST)、请求头等信息。这就像填写一张快递单,告诉OkHttp要把请求发到哪里、用什么方式发送。

然后,通过client.newCall(request)得到一个Call对象,它代表一个准备好的请求。这时候可以选择同步或异步执行。

  • 同步请求​:直接调用call.execute(),这个方法会阻塞当前线程,直到拿到服务器的响应,适合在子线程中使用。
  • 异步请求​:调用call.enqueue(callback),把请求交给后台线程池处理,通过回调返回结果,这样可以避免阻塞主线程,适合在Android中做网络请求。

面试官​:听说OkHttp的拦截器链很重要,能讲讲它的作用吗?
候选人​:
拦截器链是OkHttp最核心的设计之一,有点像流水线上的工人,每个拦截器负责处理一个特定任务。当请求发起时,这些拦截器会按顺序对请求进行处理,最后再逆序处理响应。例如:

  1. 重试拦截器​(RetryAndFollowUpInterceptor):如果请求失败了,它会自动重试或者处理重定向(比如遇到301/302状态码)。
  2. 桥接拦截器​(BridgeInterceptor):负责补充一些必要的请求头,比如Content-TypeCookie,让请求更符合HTTP协议规范。
  3. 缓存拦截器​(CacheInterceptor):根据HTTP缓存头判断是否使用本地缓存,减少重复请求。
  4. 连接拦截器​(ConnectInterceptor):管理TCP连接,复用连接池里的空闲连接,避免每次请求都重新握手。
  5. 网络拦截器​(自定义的addNetworkInterceptor):可以在这里打印请求日志,或者修改原始请求数据。
  6. 请求服务拦截器​(CallServerInterceptor):真正发送请求到服务器并读取响应数据。

面试官​:OkHttp是怎么优化性能的?比如连接复用?
候选人​:
OkHttp通过连接池​(ConnectionPool)来复用连接,这有点像“共享单车”的概念。当一个请求完成后,连接并不会立即关闭,而是被保留在池子里一段时间(默认5分钟),如果接下来有相同目标地址的请求,就可以直接复用这个连接,省去了TCP握手和TLS协商的时间,这对高频请求的场景性能提升非常明显。

另外,Dispatcher负责调度异步请求的线程池,默认支持最多64个并发请求,同时每个Host最多允许5个并发,这样既保证了效率,又防止了资源被耗尽。

面试官​:如果要添加一个日志拦截器记录请求耗时,该怎么做?
候选人​:
可以自定义一个拦截器,在intercept方法里记录请求开始和结束的时间。比如:

public class LoggingInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();long startTime = System.nanoTime();System.out.println("发送请求: " + request.url());Response response = chain.proceed(request);long duration = (System.nanoTime() - startTime) / 1e6;System.out.println("收到响应: " + response.code() + ", 耗时: " + duration + "ms");return response;}
}

然后通过OkHttpClient.Builder添加这个拦截器:

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();

这样每次请求都会输出日志,方便调试耗时和问题排查。

面试官​:同步和异步请求在底层是怎么处理的?
候选人​:

  • 同步请求​:直接在当前线程执行,由Dispatcher标记为正在运行的任务,执行完成后移除。
  • 异步请求​:会被封装成AsyncCall(本质是一个Runnable),交给Dispatcher的线程池执行。线程池默认大小是64,所以即使有大量请求,也不会无限制创建线程,避免资源竞争。

另外,OkHttp内部会优先复用空闲的线程,而不是频繁创建和销毁,这也能减少性能开销。

面试官​:如果让你设计一个网络库,会参考OkHttp的哪些设计?
候选人​:
我会借鉴它的拦截器链机制,把不同职责的功能模块化,比如日志、缓存、重试等,每个模块只关注自己的逻辑,方便扩展和维护。其次是连接池的设计,复用连接能显著提升性能。最后是建造者模式,用链式调用配置参数,代码更清晰,比如:

new MyHttpClient.Builder().timeout(10, TimeUnit.SECONDS).enableCache(true).build();

这种设计对开发者更友好,避免了冗长的构造函数参数列表。


OkHttp核心机制通俗解析

面试官​:你对OkHttp的拦截器链了解吗?能说说它的作用吗?
候选人​:
OkHttp的拦截器链就像一条流水线,每个环节(拦截器)负责处理特定任务。比如,第一个工人(拦截器)检查包裹是否需要重试发货(比如网络断了自动重试),第二个工人给包裹贴上快递单(补充请求头),第三个工人检查仓库有没有现成的包裹(缓存拦截器),第四个工人直接调用快递公司发件(连接服务器)。

这样设计的好处是每个环节分工明确,比如我要加一个功能(比如打印日志),只需要在流水线里插一个“日志工人”,不用动其他代码,扩展性特别好。


面试官​:连接池听起来很高大上,实际有什么用?
候选人​:
连接池其实就像“共享单车”。第一次访问服务器时,OkHttp会“扫码开锁”(TCP三次握手+TLS握手),用完后不立即关锁,而是把车放回停车场(连接池),保留5分钟。如果5分钟内有人要访问同一个服务器(比如同一个域名),就直接骑这辆车,省去了重复开锁的时间,特别适合高频请求的场景,比如APP里反复调用同一个API接口。


面试官​:建造者模式在OkHttp里是怎么体现的?
候选人​:
举个例子,就像去奶茶店点单。如果奶茶有20种配料(超时时间、拦截器、代理等),用传统方式点单得说:“我要一杯奶茶,加10秒超时、30秒读取超时、加珍珠、加椰果...” 听着就头疼。而建造者模式就像店员给你一张菜单,你只需要勾选需要的选项:

OkHttpClient client = new OkHttpClient.Builder()  // 拿到菜单.connectTimeout(10, TimeUnit.SECONDS)         // 勾选“连接超时10秒”.addInterceptor(new LoggingInterceptor())     // 勾选“加日志”.build();                                     // 下单

这样代码清晰,避免参数爆炸,也方便后期维护(比如调整超时时间不用翻构造函数文档)。


面试官​:如果让你设计一个拦截器统计网络请求耗时,你会怎么做?
候选人​:
我会在拦截器里记录请求开始的时间戳,等拿到服务器响应后,用当前时间减去开始时间,就是耗时。比如:

public class TimerInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {long startTime = System.currentTimeMillis();  // 开始时间Request request = chain.request();Response response = chain.proceed(request);   // 放行请求long cost = System.currentTimeMillis() - startTime;Log.d("网络耗时", request.url() + " : " + cost + "ms");return response;}
}

然后把这个拦截器加到OkHttpClient里,就像给所有网络请求装了个计时器,上线后能快速发现哪些接口性能差。


面试官​:连接池的默认配置是什么?哪些情况需要调整?
候选人​:
默认最多保留5个空闲连接,存活5分钟。如果我们的APP需要频繁访问多个不同域名(比如同时调支付接口、地图接口、用户中心接口),可以适当调大连接数,比如改成10个:

ConnectionPool pool = new ConnectionPool(10, 5, TimeUnit.MINUTES);
OkHttpClient client = new OkHttpClient.Builder().connectionPool(pool).build();

但也不能无脑调大,连接数太多会占用内存,一般要根据实际场景测试。


面试官​:为什么OkHttpClient建议全局单例?
候选人​:
主要有两个原因:

  1. 连接池复用​:如果每个请求都new一个OkHttpClient,连接池就形同虚设,每次都要重新建连,性能大打折扣。
  2. 资源开销​:每个Client都有自己的线程池、连接池,多实例会导致内存和CPU浪费,甚至引发线程数爆炸的问题。

这就像公司里每个部门都自己买打印机,不仅贵,还浪费电。不如整个公司共用几台打印机(全局单例),既省钱又高效。


基础知识扩展:

OkHttp三大核心机制

一、拦截器链机制:责任链模式的完美实践

1. 设计思想与工作原理

OkHttp的拦截器链是其架构的“灵魂”,采用责任链模式将复杂网络请求拆解为多个独立模块,每个拦截器(Interceptor)专注单一职责,通过链式传递实现高效协作。

  • 请求处理流程​:从第一个拦截器开始,依次对请求进行加工(如添加Header、压缩数据),最终由CallServerInterceptor发送到服务器。
  • 响应处理流程​:响应从最后一个拦截器反向传递,每个拦截器可对响应进行后处理(如解压数据、缓存结果)。

源码流程示例​:

// RealCall.java
Response getResponseWithInterceptorChain() {List<Interceptor> interceptors = new ArrayList<>();interceptors.add(new RetryAndFollowUpInterceptor()); // 重试与重定向interceptors.add(new BridgeInterceptor());         // 补充协议头interceptors.add(new CacheInterceptor());          // 缓存处理interceptors.add(new ConnectInterceptor());        // 连接管理interceptors.add(new CallServerInterceptor());     // 发送请求// 构建责任链Interceptor.Chain chain = new RealInterceptorChain(interceptors, ...);return chain.proceed(request);
}
2. 关键拦截器详解
拦截器核心职责应用场景示例
RetryAndFollowUpInterceptor处理请求失败后的重试逻辑(如网络波动),自动处理3xx重定向响应网络不稳定时自动重试,避免手动处理
BridgeInterceptor补充协议头(如Content-LengthUser-Agent),处理Cookie将开发者友好的Request转为HTTP标准请求
CacheInterceptor根据缓存策略(如Cache-Control)返回缓存或写入新缓存实现离线缓存,减少重复请求
ConnectInterceptor复用连接池中的TCP连接或创建新连接,完成TLS握手提升高频请求性能
CallServerInterceptor通过Socket发送请求数据,读取服务器响应实际网络IO操作
3. 自定义拦截器实战

场景​:需要全局监控请求耗时和错误日志。
实现代码​:

public class MetricsInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();long startNs = System.nanoTime();try {Response response = chain.proceed(request);long costMs = (System.nanoTime() - startNs) / 1_000_000;logSuccess(request.url(), response.code(), costMs);return response;} catch (IOException e) {long costMs = (System.nanoTime() - startNs) / 1_000_000;logError(request.url(), e, costMs);throw e;}}private void logSuccess(HttpUrl url, int code, long costMs) {System.out.printf("请求成功: %s | 状态码: %d | 耗时: %dms\n", url, code, costMs);}private void logError(HttpUrl url, Exception e, long costMs) {System.err.printf("请求失败: %s | 错误: %s | 耗时: %dms\n", url, e.getMessage(), costMs);}
}

使用方式​:

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new MetricsInterceptor()) // 全局生效.build();
4. 高频面试问题

Q1: 应用拦截器(addInterceptor)和网络拦截器(addNetworkInterceptor)有何区别?
A:

  • 应用拦截器​:在CacheInterceptor之前执行,只触发一次,无法感知重定向和重试。
    适用场景​:添加全局Header、请求日志统计。
  • 网络拦截器​:在ConnectInterceptor之后执行,可能因重试触发多次。
    适用场景​:修改原始请求数据(如加密Body)、网络层日志。

Q2: 拦截器执行顺序是怎样的?
A: 顺序为:用户自定义应用拦截器 → Retry → Bridge → Cache → Connect → 用户自定义网络拦截器 → CallServer。


二、连接池:性能优化的基石

1. 核心机制解析
  • 复用原理​:对相同Address(相同域名、端口、代理等)的请求复用TCP连接,跳过三次握手(节省约200ms)和TLS握手(节省约300ms)。
  • 默认配置​:
    • 最大空闲连接数:5
    • 空闲连接存活时间:5分钟
  • 配置调优​:
    // 调大连接池参数(适合高频请求场景)
    ConnectionPool pool = new ConnectionPool(10,     // 最大空闲连接数2,      // 存活时间TimeUnit.MINUTES
    );
    OkHttpClient client = new OkHttpClient.Builder().connectionPool(pool).build();
2. 连接复用流程图解
   Request 1 → 创建连接 → 完成请求 → 放入连接池(保持5分钟)Request 2 → 检查连接池 → 找到相同Address连接 → 复用Request 3 → 检查连接池 → 无可用连接 → 创建新连接
3. 维护机制
  • 清理策略​:后台线程每隔cleanupInterval(默认100ms)检查一次,关闭超过最大空闲数或超时的连接。
  • 核心源码片段​:
    // ConnectionPool.java
    void cleanup(long now) {// 遍历所有连接,标记过期连接for (RealConnection connection : connections) {if (connection.idleAtNs + keepAliveDurationNs < now) {expiredConnections.add(connection);}}// 关闭过期连接for (RealConnection connection : expiredConnections) {connections.remove(connection);}
    }
4. 高频面试问题

Q: OkHttp如何判断两个请求可以复用同一个TCP连接?
A: 通过Address的五个维度匹配:

  1. 域名(host)
  2. 端口(port)
  3. 代理配置(Proxy)
  4. TLS配置(如证书、TLS版本)
  5. 路由信息(如DNS解析后的IP地址)

三、建造者模式:灵活配置的利器

1. 设计动机
  • 问题​:传统构造方法参数爆炸(OkHttpClient有20+配置项),难以维护。
  • 解决​:通过链式调用逐步设置参数,提升可读性和扩展性。
2. OkHttp中的建造者模式
  • OkHttpClient配置示例​:
    OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS)   // 连接超时.readTimeout(30, TimeUnit.SECONDS)      // 读取超时.writeTimeout(30, TimeUnit.SECONDS)     // 写入超时.addInterceptor(new LoggingInterceptor()) // 拦截器.connectionPool(customPool)             // 自定义连接池.build();
  • Request构建示例​:
    Request request = new Request.Builder().url("https://api.example.com/data").header("Authorization", "Bearer token").post(RequestBody.create("{\"key\":\"value\"}", JSON)).build();
3. 源码实现剖析
  • Builder类结构​:
    public class OkHttpClient {public static class Builder {private int connectTimeout;private int readTimeout;private List<Interceptor> interceptors = new ArrayList<>();public Builder connectTimeout(long timeout, TimeUnit unit) {this.connectTimeout = unit.toMillis(timeout);return this; // 返回this实现链式调用}public OkHttpClient build() {return new OkHttpClient(this);}}
    }
4. 高频面试问题

Q: 为什么OkHttpClient要设计成不可变对象?
A:

  • 线程安全​:配置一旦创建无法修改,多线程环境下无需同步。
  • 明确语义​:避免运行时动态修改配置导致的意外行为。
  • 性能优化​:可缓存已构建的Client实例,减少重复初始化开销。

Retrofit核心技术解析

1. 注解机制:用“标签”描述请求

面试官​:能说说Retrofit的注解是怎么用的吗?
候选人​:
Retrofit的注解就像给网络请求贴标签。比如我想定义一个获取用户信息的接口,可以这样写:
@GET("users/{username}")” 这个标签告诉Retrofit:“这是个GET请求,路径是users/用户名”。而方法参数上的“@Path("username")”就像把用户名填到路径的空格里。
这些标签让代码变得像说明书一样清晰,Retrofit看到这些标签就知道怎么组装请求,开发者只需要关心业务逻辑,不用操心网络请求的细节。


2. 动态代理:背后的“智能秘书”​

面试官​:听说Retrofit用了动态代理,这是什么意思?
候选人​:
动态代理就像雇了一个聪明的秘书。当调用retrofit.create(UserService.class)时,Retrofit会悄悄生成一个实现了接口的代理对象。
比如我调用userService.getProfile("Alice"),秘书会立刻行动:

  1. 查看方法上的标签(比如@GET确定请求方式)
  2. 把参数"Alice"塞到路径的对应位置
  3. 把完整的请求交给OkHttp去执行
    整个过程完全自动化,开发者就像老板一样,只需要下指令,秘书会处理所有杂事。

3. 与OkHttp合作:黄金搭档的分工

面试官​:Retrofit和OkHttp是什么关系?
候选人​:
它们就像设计师和工程师的完美组合。Retrofit负责设计蓝图——用注解定义请求结构,OkHttp负责实际施工——处理网络连接、数据收发这些脏活累活。
比如要监控网络请求,可以给OkHttp装个“行车记录仪”(添加日志拦截器):
client.addInterceptor(new HttpLoggingInterceptor())
这样所有经过Retrofit的请求都会自动记录日志,就像给每个网络请求配了个黑匣子,调试的时候一目了然。


4. 数据转换:自动翻译官

面试官​:怎么把服务器返回的JSON转成对象?
候选人​:
Retrofit有个“翻译官团队”(转换器工厂)。比如用Gson转换器时:
addConverterFactory(GsonConverterFactory.create())
服务器返回的JSON数据会被自动解析成Java对象,就像有个翻译实时把外语合同翻成中文。如果哪天要换XML格式,只需要换个翻译官(换个转换器),业务代码完全不用改。


5. 生命周期管理:避免“僵尸请求”​

面试官​:怎么防止Activity销毁后请求还在跑?
候选人​:
这就像订外卖后突然要出门,得记得取消订单。用Retrofit时,可以在Activity的onDestroy里调用:
call.cancel()
如果是用RxJava,可以用一个“收纳盒”(CompositeDisposable)管理所有请求:
disposables.add(request)
当页面销毁时清空收纳盒:“disposables.clear()”,这样所有进行中的请求都会自动取消,避免浪费资源和内存泄漏。


6. 缓存机制:省流量神器

面试官​:怎么让APP没网时也能显示数据?
候选人​:
Retrofit通过OkHttp支持智能缓存。就像给APP配了个临时储物柜,配置好缓存大小后:
client.cache(new Cache(directory, size))
服务器响应如果带着“Cache-Control: max-age=60”这样的头,OkHttp就会把数据存1分钟。下次同样请求会优先从储物柜取数据,既省流量又让APP更流畅,特别是在网络不稳定时体验更好。


7. 适配RxJava:异步流水线

面试官​:怎么用Retrofit配合RxJava?
候选人​:
加个“适配插座”就行:
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
这样接口方法可以直接返回RxJava的Observable,用操作符处理异步请求就像组装流水线:
.subscribeOn(Schedulers.io())”在后台线程发请求
.observeOn(AndroidSchedulers.mainThread())”在主线程更新UI
配合retryWhen()还能实现自动重试,像给网络请求加了保险机制。


OkHttp与Retrofit的黄金搭档关系

面试官​:能说说OkHttp和Retrofit是怎么配合工作的吗?它们各自负责什么?
候选人​:
可以啊!这俩库的关系就像“厨师”和“服务员”的合作。

Retrofit是服务员​:
它的核心工作是“把复杂的点餐流程标准化”。比如你走进餐厅,服务员会给你菜单(定义接口),你只需要说“我要一份牛排七分熟”(用@GET注解描述请求),服务员就能把你的需求翻译成厨房能理解的指令(生成HTTP请求)。

  • 服务员做的事​:
    • 用注解解析你的需求(@GET@POST@Path等)。
    • 把Java对象转成网络请求参数(比如把User对象转成JSON)。
    • 把服务器返回的数据转回Java对象(比如JSON转成List<User>)。

OkHttp是后厨的厨师​:
它负责实际“烹饪”和“送餐”。服务员把订单交给厨师后,厨师会处理所有底层细节:

  • 厨师做的事​:
    • 管理火候(TCP连接池复用,省去重复握手)。
    • 处理突发情况(比如网络断了自动重试)。
    • 控制上菜速度(超时设置、流量控制)。
    • 记录每道菜的日志(通过拦截器打印请求详情)。

面试官​:那它们具体是怎么“传纸条”的?比如一个网络请求的流程?
候选人​:
举个实际例子吧!假设我们调用一个获取用户信息的接口:

// Retrofit接口定义(服务员记下订单)
@GET("users/{id}")
Call<User> getUser(@Path("id") String userId);// 调用代码(顾客下单)
Call<User> call = service.getUser("123");
call.enqueue(...);

具体流程​:

  1. 服务员接单​:
    Retrofit看到@GET("users/{id}"),知道这是一个GET请求,路径是users/123(把{id}替换成"123")。

  2. 服务员写菜单​:
    Retrofit把方法参数、注解信息打包成一个“订单”(构建OkHttp的Request对象)。

  3. 订单递到后厨​:
    Retrofit调用OkHttpClient(厨师)来执行这个Request

  4. 厨师开始烹饪​:
    OkHttp检查是否有现成的连接(连接池复用),如果没有就建立新连接。
    发送请求,处理重试、缓存(比如服务器返回304就用本地缓存)。

  5. 出菜和回传​:
    厨师把做好的菜(服务器响应)交给服务员,Retrofit用GsonConverter把JSON数据转成User对象(就像把生牛排煎成七分熟)。

  6. 服务员上菜​:
    Retrofit通过Callback把最终结果返回给调用方,或者抛异常提示“牛排煎糊了”(网络错误)。


面试官​:如果我需要定制化功能,比如加请求头、打印日志,该用谁?
候选人​:
这得看需求属于哪个环节:

  • Retrofit的领域​(服务员能处理的):

    • 全局添加请求头(比如@Headers("Authorization: token"))。
    • 统一处理响应(比如用CallAdapterCall<User>转成LiveData<User>)。
  • OkHttp的领域​(需要厨师配合的):

    • 打印网络请求日志(用HttpLoggingInterceptor,这是OkHttp的拦截器)。
    • 缓存策略(配置OkHttp的Cache对象)。
    • 模拟网络延迟(写个拦截器故意延迟响应,方便测试)。

举个例子,如果要给所有请求加一个User-Agent头:

// 用OkHttp的拦截器(厨师的工具)
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(chain -> {Request request = chain.request().newBuilder().header("User-Agent", "MyApp/1.0").build();return chain.proceed(request);}).build();// 把这个厨师交给服务员
Retrofit retrofit = new Retrofit.Builder().client(client).baseUrl("https://api.example.com/").build();

这相当于告诉厨师:“每道菜出锅前,记得贴个‘MyApp’的标签”。


面试官​:为什么Retrofit不自己处理网络请求,非要依赖OkHttp?
候选人​:
这就像餐厅为什么要分服务员和厨师——专业的人做专业的事

  • Retrofit的强项是简化接口定义数据转换,让代码更优雅。
  • OkHttp的强项是高性能网络通信,它解决了连接池、缓存、拦截器这些复杂问题。

如果Retrofit自己实现网络层,就得重复造轮子,而且很难做到OkHttp多年的优化积累(比如HTTP/2支持、WebSocket等)。两者的分工让开发者既能享受Retrofit的简洁,又能用OkHttp的强悍性能。


面试官​:能举个比喻总结它们的关系吗?
候选人​:
可以!Retrofit就像“外卖平台”,OkHttp就像“快递小哥”。

  • 你通过外卖平台(Retrofit)下单,选好餐厅、菜品(定义接口)。
  • 平台把订单打包成标准化格式(生成Request)。
  • 快递小哥(OkHttp)根据订单内容,选择最优路线(连接复用),处理突发问题(重试、超时),最后把餐送到你手里(返回Response)。

没有快递小哥,外卖平台就是个空壳;没有平台,小哥得手动接电话记地址。两者结合,才能让你“躺着点外卖”。

相关文章:

Android-OkHttp与Retrofit学习总结

OkHttp核心机制与工作流程​ ​面试官​&#xff1a;能简单介绍一下OkHttp的工作流程吗&#xff1f; ​候选人​&#xff1a; 好的&#xff0c;OkHttp的工作流程大致可以分为几个步骤。首先&#xff0c;我们需要创建一个OkHttpClient实例&#xff0c;通常会用建造者模式来配置…...

移远三款主流5G模块RM500U,RM520N,RG200U比较

文章目录 概要一、技术架构差异1. ‌3GPP协议版本‌2. ‌芯片平台与性能‌3. ‌频段覆盖与区域适配‌4. 协议增强与特殊功能‌ 二、功能与应用定位1. ‌网络兼容性‌2. ‌封装与接口扩展‌ 三、典型应用场景总结 概要 本文介绍下移远两款主流5G模块RM500U RM520N RG200U。 一…...

C++引用以及和指针的区别

C++ 引用 引用(reference)是 C++ 中的一种变量类型,是另一个变量的别名。一旦引用被初始化,就不能再改变它所指向的对象。 引用的特点 必须初始化:声明引用时必须立即对其进行初始化。不可更改绑定:一旦引用绑定到某个变量,就不能再指向其他变量。语法简洁:使用引用不…...

firfox 国外版和国内版本账号不互通问题处理

https://blog.csdn.net/sinat_37891718/article/details/147445621 现在国际服的火狐浏览器修改使用国内的账号服务器&#xff0c;需要先在搜索框输入about:config 中改变三项配置&#xff0c;然后重启浏览器&#xff0c;才能正常使用国内的火狐账号服务器 identity.fxaccount…...

Linux基本指令篇 —— whoami指令

whoami 是 Linux 和 Unix 系统中一个简单但实用的命令&#xff0c;全称 Who Am I&#xff08;我是谁&#xff09;。它的功能是显示当前登录用户的用户名。以下是关于 whoami 的详细解析&#xff1a; 目录 1. 基本用法 2. 命令特点 3. 实际应用场景 场景 1&#xff1a;脚本中…...

用go从零构建写一个RPC(3)--异步调用+多路复用实现

在前两个版本中&#xff0c;我们实现了基础的客户端-服务端通信、连接池、序列化等关键模块。为了进一步提升吞吐量和并发性能&#xff0c;本版本新增了 异步发送机制 和 多路复用支持&#xff0c;旨在减少资源消耗、提升连接利用率。 代码地址&#xff1a;https://github.com/…...

力扣395做题笔记

题目链接 力扣395 第一次尝试 class Solution {public int longestSubstring(String str, int k) {char[] s str.toCharArray();int n s.length;int[] cnts new int[256];int ans 0;for (int r 0, l 0; r < n; r ) { cnts[s[r]];if (cnts[s[r]] > k) { ans Mat…...

Python-numpy中常用的统计函数及转换函数

numpy中常用的统计函数 numpy中常用统计函数numpy普通统计函数忽略 NaN 值进行统计百分位数 numpy中形状转换函数重塑数组&#xff08;reshape&#xff09;展平数组&#xff08;flatten/ravel&#xff09;转置&#xff08;transpose/T&#xff09; 数据类型的转换使用astype()转…...

【C语言干货】free细节

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、为啥*phead free掉了之后&#xff0c;为啥下面还 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供 可以用&#xff1f; 前言参考 一、为…...

网络安全-等级保护(等保) 2-0 等级保护制度现行技术标准

################################################################################ 第二章&#xff1a;现行等保标准要求&#xff0c;通过表格方式详细拆分了等保的相关要求。 GB 17859-1999 计算机信息系统 安全保护等级划分准则【现行】 GB/T22240-2020 《信息安全技术 网…...

WebSocket(看这一篇就够了)

文章目录 WebSocket 基本概念什么是WebSocket?为什么需要 WebSocket&#xff1f;与 HTTP 协议的区别WebSocket协议的原理WebSocket工作流程WebSocket 数据帧结构和控制帧结构。JavaScript 中 WebSocket 对象的属性和方法&#xff0c;以及如何创建和连接 WebSocket。webSocket简…...

旧物回收小程序:让闲置焕发光彩,为生活增添价值

你是否常常为家中堆积如山的闲置物品而烦恼&#xff1f;那些曾经心爱的物品&#xff0c;如今却成了占据空间的“鸡肋”&#xff0c;丢弃可惜&#xff0c;留着又无处安放。别担心&#xff0c;一款旧物二手回收小程序将为你解决这一难题&#xff0c;让闲置物品重新焕发光彩&#…...

精益数据分析(73/126):黏性阶段的功能优先级法则——七问决策模型与风险控制

精益数据分析&#xff08;73/126&#xff09;&#xff1a;黏性阶段的功能优先级法则——七问决策模型与风险控制 在创业的黏性阶段&#xff0c;如何从海量的功能创意中筛选出真正能提升用户留存的关键改动&#xff1f;今天&#xff0c;我们结合《精益数据分析》中的“开发功能…...

React声明式编程(手动控制,大型项目,深度定制)与Vue响应式系统(自动优化,中小型项目,快速开发)区别

文章目录 React声明式与Vue响应式区别详解一、响应式机制原理对比1.1 Vue的响应式系统Vue响应式流程图Vue响应式代码示例 1.2 React的声明式更新React声明式流程图React声明式代码示例 二、更新触发逻辑差异2.1 Vue的自动更新Vue依赖收集机制 2.2 React的手动更新React Diff算法…...

数学建模MathAI智能体-2025电工杯A题实战

题目&#xff1a; 光伏电站发电功率日前预测问题 光伏发电是通过半导体材料的光电效应&#xff0c;将太阳能直接转化为电能的技术。光伏电站是由众多光伏发电单元组成的规模化发电设施。 光伏电站的发电功率主要由光伏板表面接收到的太阳辐射总量决定&#xff0c;不同季节太阳…...

跨平台游戏引擎 Axmol-2.6.0 发布

Axmol 2.6.0 版本是一个以错误修复和功能改进为主的次要LTS长期支持版本 &#x1f64f;感谢所有贡献者及财务赞助者&#xff1a;scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox、DelinWorks 相对于2.5.0版本的重要变更&#xff1a; 通…...

C# Windows Forms应用程序-002

目录 项目结构 主类和命名空间 构造函数和析构函数 初始化组件 (InitializeComponent) 按钮点击事件处理程序 主程序入口点 项目截图&#xff1a; 完整代码&#xff1a; 项目结构 这个项目是一个简单的C# Windows Forms应用程序&#xff0c;获取指定文件的根信息…...

理解计算机系统_线程(八):并行

前言 以<深入理解计算机系统>(以下称“本书”)内容为基础&#xff0c;对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定 引入 接续理解计算机系统_并发编程(10)_线程(七):基于预线程化的…...

【MySQL】09.索引

索引是用来提高数据库的性能的&#xff0c;但查询速度的提高是以插入、更新、删除的速度为代价的&#xff0c;这些写操作&#xff0c;增加了大量的IO。所以它的价值在于提高一个海量数据的检索速度。 1. 认识磁盘 MySQL 给用户提供存储服务&#xff0c;而存储的都是数据&…...

【备忘】 windows 11安装 AdGuardHome,实现开机自启,使用 DoH

windows 11安装 AdGuardHome&#xff0c;实现开机自启&#xff0c;使用 DoH 下载 AdGuardHome解压 AdGuardHome启动 AdGuard Home设置 AdGuardHome设置开机自启安装 NSSM设置开机自启重启电脑后我们可以访问 **http://127.0.0.1/** 设置使用 AdGuardHome DNS 效果图 下载 AdGua…...

[Windows] 游戏常用运行库- Game Runtime Libraries Package(6.2.25.0409)

游戏常用运行库 合集 整合了许多游戏会用到的运行库&#xff0c;支持 Windows XP – Windows 11 系统&#xff0c;并且支持自动检测系统勾选推荐的运行库&#xff0c;方便快捷。 本版特点&#xff1a; By&#xff1a;mefcl 整合常见最新游戏所需运行库 根据系统自动勾选推荐…...

MYSQL order 、group 与row_number详解

一、order by order by A ASC, B DESC,C ASC … 上述语句会先按照A排序&#xff0c;当A相同的时候再按照B排序&#xff0c;当B相同的再按照C排序&#xff0c;并会不按照ABC组合一起排序 二、group by group by A,B,C… select 中的字段必须是group by中的字段&#xff0c;…...

QT之巧用对象充当信号接收者

备注&#xff1a;以下仅为演示不代表合理性&#xff0c;适合简单任务&#xff0c;逻辑简单、临时使用&#xff0c;可保持代码简洁&#xff0c;对于复杂的任务应创建一个专门的类来管理信号和线程池任务. FileScanner类继承QObject和QRunnable&#xff0c;扫描指定目录下的文件获…...

《红警2000》游戏信息

游戏背景&#xff1a;与《红色警戒》系列的其他版本类似&#xff0c;基于红警 95 的背景设定&#xff0c;讲述了第二次世界大战期间&#xff0c;世界各国为了争夺全球霸权而展开战争。游戏画面与音效&#xff1a;在画面上相比早期的红警版本有一定提升&#xff0c;解析度更高&a…...

Vue3 + ThinkPHP8 + PHP8.x 生态与 Swoole 增强方案对比分析

一、基础方案&#xff1a;Vue3 ThinkPHP8 PHP8.x 传统架构 优点 ​成熟稳定​ 组合经过长期验证&#xff0c;文档和社区资源丰富ThinkPHP8 对PHP8.x有良好支持&#xff0c;性能比PHP7提升20-30% ​开发效率高​ TP8的ORM和路由系统大幅减少样板代码Vue3组合式API Vite开发…...

(九)PMSM驱动控制学习---高阶滑膜观测器

在之前的文章中&#xff0c;我们介绍了永磁同步电机无感控制中的滑模观测器&#xff0c;但是同时我们也认识到了他的缺点&#xff1a;因符号函数带来的高频切换分量&#xff0c;使用低通滤波器引发相位延迟&#xff1b;在本篇文章&#xff0c;我们将会介绍高阶滑模观测器的无感…...

25年上半年五月之软考之设计模式

目录 一、单例模式 二、工厂模式 三、 抽象工厂模式 四、适配器模式 五、策略模式 六、装饰器模式 ​编辑 考点&#xff1a;会挖空super(coffeOpertion); 七、代理模式 为什么必须要使用代理对象&#xff1f; 和装饰器模式的区别 八、备忘录模式 一、单例模式 这个…...

Mongo DB | 多种修改数据库名称的方式

目录 方法一&#xff1a;使用 mongodump 和 mongorestore 命令 方法二&#xff1a;使用 db.copyDatabase() 方法 方法三&#xff1a;使用 MongoDB Compass 在 MongoDB 中&#xff0c;更改数据库名称并不是一个直接的操作&#xff0c;因为 MongoDB 不提供直接重命名数据库的命…...

QListWidget的函数,信号介绍

前言 Qt版本:6.8.0 该类用于列表模型/视图 QListWidgetItem函数介绍 作用 QListWidget是Qt框架中用于管理可交互列表项的核心组件&#xff0c;主要作用包括&#xff1a; 列表项管理 支持动态添加/删除项&#xff1a;addItem(), takeItem()批量操作&#xff1a;addItems()…...

Python类属性与实例属性的覆盖机制:从Vector2d案例看灵活设计

类属性与实例属性的交互机制 Python中类属性与实例属性的关系体现了语言的动态特性。当访问一个实例属性时&#xff0c;Python会首先查找实例自身的__dict__&#xff0c;如果找不到&#xff0c;才会去查找类的__dict__。这种机制使得类属性可以优雅地作为实例属性的默认值。 …...