Java实现短信发送并校验,华为云短信配合Redis实现发送与校验

Java实现短信发送并校验,华为云短信配合Redis实现发送与校验

安装sms4j和redis

<dependency><groupId>org.dromara.sms4j</groupId><artifactId>sms4j-spring-boot-starter</artifactId><version>3.2.1</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

sms4j使用

使用sms4j可以非常简单的实现短信发送功能,并且适配了主流的云平台

官方文档地址

image-20240414174534878

添加配置

# redis配置
spring:redis:database: 0host: xxx.xxx.xxx.xxxport: 6379timeout: 1200# 发送短信的配置
sms:# 标注从yml读取配置config-type: yaml# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制restricted: true# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效account-max: 8# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效minute-max: 2# 是否打印http loghttp-log: true# 是否打印banneris-print: false# 短信厂商核心配置容纳blends:tx1:supplier: huawei#您的accessKeyaccess-key-id: 您的accessKey#您的accessKeySecretaccess-key-secret:您的accessKeySecret#您的短信签名signature: 您的短信签名#模板ID 非必须配置,如果使用sendMessage的快速发送需此配置template-id: 模板ID# 通道号sender: 通道号# 配置Idconfig-id: tx1#华为回调地址,如不需要可不设置或为空statusCallBack:#华为分配的app请求地址url: https://smsapi.cn-north-4.myhuaweicloud.com:443

accessKey、accessKeySecret等可以在华为云控制台,短信服务-我的应用查看

template-id、sender 在短信模板审核通过后可以获知

更多配置可见:https://sms4j.com/doc3/config.html

发送短信

新建一个 SmsController,这里编写了两个接口,一个用于发送短信,一个用于验证短信。

这里用到了Redis做验证码有效期缓存,缓存5分钟

还用到了 SmsUtils.getRandomInt(6) 生成一个6位数纯数字的随机数,这个 SmsUtilsorg.dromara.sms4j 内置提供好的

package com.szx.edu.controller;import com.szx.commonutils.Msg;
import com.szx.edu.utils.redisUtil.RedisServiceImpl;
import io.swagger.annotations.Api;
import org.apache.commons.lang.StringUtils;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** @author songzx* @create 2024-04-12 21:22*/
@Api(tags = "发送短信")
@RestController
@RequestMapping("/sms")
public class SmsController {@AutowiredRedisServiceImpl redisService;@GetMapping("getCode")public Msg getCode(String phone){// 1.生成一个随机验证码String randomCode = SmsUtils.getRandomInt(6);// 2.获取指定配置SmsBlend smsBlend = SmsFactory.getSmsBlend();// 3.发送短信SmsResponse smsResponse = smsBlend.sendMessage(phone,randomCode);// 4.判断是否成功boolean success = smsResponse.isSuccess();if(success){// 如果发送成功,再吧验证码保存到缓存中,并设置缓存时长300秒redisService.cacheValue(phone,randomCode,300);return Msg.Ok();}else{return Msg.Error().msg("验证码发送失败");}}@PostMapping("checkCode")public Msg checkCode(String phone,String code){// 获取缓存的codeString cacheCode = redisService.getValue(phone);// 判断得到的code和用户传递进来的code是否一样if(StringUtils.isNotEmpty(cacheCode) && code.equals(cacheCode)){return Msg.Ok().data("status","ok");}else{return Msg.Error().msg("请输入正确的验证码");}}
}

Redis配置

添加配置类

新建 RedisConfig

package com.szx.edu.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;/*** @author songzx* @create 2024-03-08 10:58*/
@EnableCaching
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600))//变双冒号为单冒号.computePrefixWith(name -> name +":").serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}
}

添加工具类

新建 RedisService,这个文件是 interface 类型

package com.szx.edu.utils.redisUtil;import org.springframework.data.redis.core.ListOperations;import java.util.List;
import java.util.Set;/*** @author songzx* @create 2024-03-08 10:49*/
public interface RedisService {/*** 添加 key:string 缓存** @param key    key* @param value    value* @param time time* @return*/boolean cacheValue(String key, String value, long time);/*** 添加 key:string 缓存** @param key   key* @param value value* @return*/boolean cacheValue(String key, String value);/*** 根据 key:string 判断缓存是否存在** @param key key* @return boolean*/boolean containsValueKey(String key);/*** 判断缓存 key:set集合 是否存在** @param key key* @return*/boolean containsSetKey(String key);/*** 判断缓存 key:list集合 是否存在** @param key key* @return boolean*/boolean containsListKey(String key);/*** 查询缓存 key 是否存在* @param key key* @return true/false*/boolean containsKey(String key);/*** 根据 key 获取缓存value** @param key key* @return value*/String getValue(String key);/*** 根据 key 移除 value 缓存** @param key key* @return true/false*/boolean removeValue(String key);/*** 根据 key 移除 set 缓存** @param key key* @return true/false*/boolean removeSet(String key);/*** 根据 key 移除 list 缓存** @param key key* @return true/false*/boolean removeList(String key);/*** 缓存set操作** @param key    key* @param value    value* @param time time* @return boolean*/boolean cacheSet(String key, String value, long time);/*** 添加 set 缓存** @param key   key* @param value value* @return true/false*/boolean cacheSet(String key, String value);/*** 添加 缓存 set** @param k    key* @param v    value* @param time 时间* @return*/boolean cacheSet(String k, Set<String> v, long time);/*** 缓存 set* @param k key* @param v value* @return*/boolean cacheSet(String k, Set<String> v);/*** 获取缓存set数据* @param k key* @return set集合*/Set<String> getSet(String k);/*** list 缓存* @param k key* @param v value* @param time 时间* @return true/false*/boolean cacheList(String k, String v, long time);/*** 缓存 list* @param k key* @param v value* @return true/false*/boolean cacheList(String k, String v);/*** 缓存 list 集合* @param k key* @param v value* @param time 时间* @return*/boolean cacheList(String k, List<String> v, long time);/***  缓存 list* @param k key* @param v value* @return true/false*/boolean cacheList(String k, List<String> v);/*** 根据 key 获取 list 缓存* @param k key* @param start 开始* @param end 结束* @return 获取缓存区间内 所有value*/List<String> getList(String k, long start, long end);/*** 根据 key 获取总条数 用于分页* @param key key* @return 条数*/long getListSize(String key);/*** 获取总条数 用于分页* @param listOps =redisTemplate.opsForList();* @param k key* @return size*/long getListSize(ListOperations<String, String> listOps, String k);/*** 根据 key 移除 list 缓存* @param k key* @return*/boolean removeOneOfList(String k);
}

然后添加接口实现类

RedisServiceImpl

package com.szx.edu.utils.redisUtil;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** @author songzx* @create 2024-03-08 10:50*/
@Service
public class RedisServiceImpl implements RedisService {/*** slf4j 日志*/private final Logger log = LoggerFactory.getLogger(this.getClass());/*** 自定义 key 三种*  String key:String value         普通key:value*  String key:Set<String> set      key:set集合*  String key:List<String> list    key:list集合*/private static final String KEY_PREFIX_KEY = "info:bear:key";private static final String KEY_PREFIX_SET = "info:bear:set";private static final String KEY_PREFIX_LIST = "info:bear:list";private final RedisTemplate<String, String> redisTemplate;/*** 注入* @param redisTemplate 模板*/@Autowiredpublic RedisServiceImpl(RedisTemplate<String, String> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 添加 key:string 缓存** @param k    key* @param v    value* @param time time* @return*/@Overridepublic boolean cacheValue(String k, String v, long time) {try {String key = KEY_PREFIX_KEY + k;ValueOperations<String, String> ops = redisTemplate.opsForValue();ops.set(key, v);if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Throwable e) {log.error("缓存存入失败key:[{}] value:[{}]", k, v);}return false;}/*** 添加 key:string 缓存** @param key   key* @param value value* @return*/@Overridepublic boolean cacheValue(String key, String value) {return cacheValue(key, value, -1);}/*** 根据 key:string 判断缓存是否存在** @param key key* @return boolean*/@Overridepublic boolean containsValueKey(String key) {return containsKey(KEY_PREFIX_KEY + key);}/*** 判断缓存 key:set集合 是否存在** @param key key* @return*/@Overridepublic boolean containsSetKey(String key) {return containsKey(KEY_PREFIX_SET + key);}/*** 判断缓存 key:list集合 是否存在** @param key key* @return boolean*/@Overridepublic boolean containsListKey(String key) {return containsKey(KEY_PREFIX_LIST + key);}/*** 查询缓存 key 是否存在* @param key key* @return true/false*/@Overridepublic boolean containsKey(String key) {try {return redisTemplate.hasKey(key);} catch (Throwable e) {log.error("判断缓存存在失败key:[" + key + "],错误信息 Codeor[{}]", e);}return false;}/*** 根据 key 获取缓存value** @param key key* @return value*/@Overridepublic String getValue(String key) {try {ValueOperations<String, String> ops = redisTemplate.opsForValue();return ops.get(KEY_PREFIX_KEY + key);} catch (Throwable e) {log.error("根据 key 获取缓存失败,当前key:[{}],失败原因 Codeor:[{}]", key, e);}return null;}/*** 缓存set操作** @param k    key* @param v    value* @param time time* @return boolean*/@Overridepublic boolean cacheSet(String k, String v, long time) {try {String key = KEY_PREFIX_SET + k;SetOperations<String, String> opsForSet = redisTemplate.opsForSet();opsForSet.add(key, v);if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Throwable e) {log.error("缓存 set 失败 当前 key:[{}] 失败原因 [{}]", k, e);}return false;}/*** 添加 set 缓存** @param key   key* @param value value* @return true/false*/@Overridepublic boolean cacheSet(String key, String value) {return cacheSet(key, value, -1);}/*** 添加 缓存 set** @param k    key* @param v    value* @param time 时间* @return*/@Overridepublic boolean cacheSet(String k, Set<String> v, long time) {try {String key = KEY_PREFIX_SET + k;SetOperations<String, String> opsForSet = redisTemplate.opsForSet();opsForSet.add(key, v.toArray(new String[v.size()]));if (time > 0){redisTemplate.expire(key,time,TimeUnit.SECONDS);}return true;} catch (Throwable e) {log.error("缓存 set 失败 当前 key:[{}],失败原因 [{}]", k, e);}return false;}/*** 缓存 set* @param k key* @param v value* @return*/@Overridepublic boolean cacheSet(String k, Set<String> v) {return cacheSet(k,v,-1);}/*** 获取缓存set数据* @param k key* @return set集合*/@Overridepublic Set<String> getSet(String k) {try {String key = KEY_PREFIX_SET + k;SetOperations<String, String> opsForSet = redisTemplate.opsForSet();return opsForSet.members(key);}catch (Throwable e){log.error("获取缓存set失败 当前 key:[{}],失败原因 [{}]", k, e);}return null;}/*** list 缓存* @param k key* @param v value* @param time 时间* @return true/false*/@Overridepublic boolean cacheList(String k, String v, long time) {try {String key = KEY_PREFIX_LIST + k;ListOperations<String, String> opsForList = redisTemplate.opsForList();//此处为right push 方法/ 也可以 left push ..opsForList.rightPush(key,v);if (time > 0){redisTemplate.expire(key,time,TimeUnit.SECONDS);}return true;}catch (Throwable e){log.error("缓存list失败 当前 key:[{}],失败原因 [{}]", k, e);}return false;}/*** 缓存 list* @param k key* @param v value* @return true/false*/@Overridepublic boolean cacheList(String k, String v) {return cacheList(k,v,-1);}/*** 缓存 list 集合* @param k key* @param v value* @param time 时间* @return*/@Overridepublic boolean cacheList(String k, List<String> v, long time) {try {String key = KEY_PREFIX_LIST + k;ListOperations<String, String> opsForList = redisTemplate.opsForList();opsForList.rightPushAll(key,v);if (time > 0){redisTemplate.expire(key,time,TimeUnit.SECONDS);}return true;}catch (Throwable e){log.error("缓存list失败 当前 key:[{}],失败原因 [{}]", k, e);}return false;}/***  缓存 list* @param k key* @param v value* @return true/false*/@Overridepublic boolean cacheList(String k, List<String> v) {return cacheList(k,v,-1);}/*** 根据 key 获取 list 缓存* @param k key* @param start 开始* @param end 结束* @return 获取缓存区间内 所有value*/@Overridepublic List<String> getList(String k, long start, long end) {try {String key = KEY_PREFIX_LIST + k;ListOperations<String, String> opsForList = redisTemplate.opsForList();return opsForList.range(key,start,end);}catch (Throwable e){log.error("获取list缓存失败 当前 key:[{}],失败原因 [{}]", k, e);}return null;}/*** 根据 key 获取总条数 用于分页* @param key key* @return 条数*/@Overridepublic long getListSize(String key) {try {ListOperations<String, String> opsForList = redisTemplate.opsForList();return opsForList.size(KEY_PREFIX_LIST + key);}catch (Throwable e){log.error("获取list长度失败key[" + KEY_PREFIX_LIST + key + "], Codeor[" + e + "]");}return 0;}/*** 获取总条数 用于分页* @param listOps =redisTemplate.opsForList();* @param k key* @return size*/@Overridepublic long getListSize(ListOperations<String, String> listOps, String k) {try {return listOps.size(k);}catch (Throwable e){log.error("获取list长度失败key[" + KEY_PREFIX_LIST + k + "], Codeor[" + e + "]");}return 0;}/*** 根据 key 移除 list 缓存* @param k key* @return*/@Overridepublic boolean removeOneOfList(String k) {try {String key = KEY_PREFIX_LIST + k;ListOperations<String, String> opsForList = redisTemplate.opsForList();opsForList.rightPop(key);return true;}catch (Throwable e){log.error("移除list缓存失败 key[" + KEY_PREFIX_LIST + k + "], Codeor[" + e + "]");}return false;}/*** 根据 key 移除 value 缓存** @param key key* @return true/false*/@Overridepublic boolean removeValue(String key) {return remove(KEY_PREFIX_KEY + key);}/*** 根据 key 移除 set 缓存** @param key key* @return true/false*/@Overridepublic boolean removeSet(String key) {return remove(KEY_PREFIX_SET + key);}/*** 根据 key 移除 list 缓存** @param key key* @return true/false*/@Overridepublic boolean removeList(String key) {return remove(KEY_PREFIX_LIST + key);}/*** 移除缓存** @param key key* @return boolean*/private boolean remove(String key) {try {redisTemplate.delete(key);return true;} catch (Throwable e) {log.error("移除缓存失败 key:[{}] 失败原因 [{}]", key, e);}return false;}
}

测试接口

首先测试发送短信,可以看到正常发送

image-20240414180207232

然后去Redis中查看,缓存的值是 232868

image-20240414180352288

此时我手机接收到的也是 232868

然后故意输入错误的试一试

image-20240414180554472

然后再输入正确的

image-20240414180623568

然后等待五分钟之后,刷新Redis,发现缓存的值自动删除了

image-20240414180752430

这时再去发起验证接口试一试

image-20240414180835576

可以看到,这个验证码已经不能用了

到这里我们就实现了短信的发送和验证功能

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

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

相关文章

WPS基础使用

个人笔记&#xff08;整理不易&#xff0c;有帮助&#xff0c;收藏点赞评论&#xff0c;爱你们&#xff01;&#xff01;&#xff01;你的支持是我写作的动力&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔…

单路高清HDMI编码器JR-3211HD

产品简介&#xff1a; JR-3211HD单路高清HDMI编码器是专业的高清音视频编码产品&#xff0c;该产品具有支持1路高清HDMI音视频采集功能&#xff0c; 1路3.5MM独立外接音频输入&#xff0c;编码输出双码流H.264格式&#xff0c;音频MP3/AAC格式。编码码率可调&#xff0c;画面质…

SHARE 203S PRO:倾斜摄影相机在地灾救援中的应用

在地质灾害的紧急关头&#xff0c;救援队伍面临的首要任务是迅速而准确地掌握灾区的地理信息。这时&#xff0c;倾斜摄影相机成为了救援测绘的利器。SHARE 203S PRO&#xff0c;这款由深圳赛尔智控科技有限公司研发的五镜头倾斜摄影相机&#xff0c;以其卓越的性能和功能&#…

每日一题:C语言经典例题之最受欢迎歌手

题目描述 学校推出了10名歌手&#xff0c;每个歌手都有唯一编号。校学生会想知道这些歌手受欢迎的程度&#xff0c;设了一个投票箱&#xff0c;让每一个同学给自己喜欢的歌手投票&#xff0c;同学们使用歌手编号进行投票。现在学生会找你帮忙统计一下每位歌手获得的票数&#x…

QtCreater 使用

QtCreater 创建项目 1.刚进入 QtCreater 的界面是这样的一个界面 ① 创建一个新的文件&#xff0c;那么我们就选择左上角的 “文件” ② 点击新建文件&#xff0c;或者也可以直接使用快捷键 CtrlN 此时就会弹出对话框&#xff0c;让我们选择想要创建的文件&#xff1a; Appli…

python爬虫-----爬虫解析—xpath(第十八天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

一体化泵站的生产制造流程怎样

诸城市鑫淼环保小编带大家了解一下一体化泵站的生产制造流程怎样 综合泵站和传统式混泥土泵站的一大差别是&#xff0c;离去制造厂前&#xff0c;能够开展全部机械设备设备的生产加工及零部件加工&#xff0c;随后运送到建筑项目当场开展安裝。这类经营方式缩短了开发周期&…

Nginx日志格式化和追踪

背景 Nginx是一款功能强大的Web服务器&#xff0c;对于网络环境中的日志记录和配置至关重要。定制化Nginx日志格式可以帮助管理员更好地监控服务器性能、分析用户行为并做出相应优化。在本文中&#xff0c;我们将深入探讨Nginx日志格式的高级定制化策略&#xff0c;包括理解基…

每日练习——leetcode77和347

目录 347. 前 K 个高频元素 题目描述 解题思路 代码实现 77. 组合 题目描述 解题思路 代码实现 347. 前 K 个高频元素 题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nu…

API接口京东开放平台item_get-获得京东商品详情API接口根据商品ID查询商品标题价格描述等详情数据

京东商品详情API接口可以提供以下方面的信息&#xff1a; 商品基础信息&#xff1a;包括商品的标题、价格、描述、图片等基本信息&#xff0c;这是构建电商平台的基础数据。商品分类信息&#xff1a;帮助用户更好地了解商品所属的类别&#xff0c;便于商品筛选和查找。商品销售…

sheng的学习笔记-AI-决策树(Decision Tree)

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 什么是决策树 划分选择 信息增益 增益率 基尼指数 剪枝处理 预剪枝 后剪枝 连续值处理 另一个例子 基本步骤 排序 计算候选划分点集合 评估分割点 每个分割点都进行评估&#xff0c;找到最大信息增益的…

DB schema表中使用全局变量及在DB组件中查询

DB schema表中使用全局变量及在DB组件中查询 规则如下&#xff1a; 使用如下&#xff1a; 如果在unicloud-db组件上不加判断条件&#xff0c;就会报错&#xff0c;并进入到登录页。 那么就会进入到登录页&#xff0c;加上了判断条件&#xff0c;有数据了就不会了。 因为在sc…

Kotlin学习ing

函数名称应采用驼峰式大小写形式&#xff0c;并且应该是动词或动词短语。val 和 var字符串模板&#xff1a;fun main() {val numberOfPhotos 100val photosDeleted 10println("$numberOfPhotos photos")println("$photosDeleted photos deleted")println…

电脑录屏软件哪个好用又免费?市面20款录屏软件测评结果

随着在线教学、远程办公和自媒体创作的兴起&#xff0c;电脑录屏软件逐渐成为了许多用户的必备工具。市面上的录屏软件琳琅满目&#xff0c;但真正既好用又免费的却并不多见。为了帮助大家找到心仪的录屏软件&#xff0c;我们对市面上20款热门免费录屏软件进行了详细的测评。 电…

一起学习python——基础篇(20)

前言&#xff0c;之前经常从网上找一些免费的接口来测试&#xff0c;有点受制于人的感觉。想了想还不如直接写一个接口&#xff0c;这样方便自己测试。自己想返回什么格式就返回什么样子&#xff0c;不用担心服务报错&#xff0c;因为自己就可以完全掌控。然后宿舍二哥告诉我py…

Android笔记--MediaCodec(二)

这一节主要了解MediaCodec处理音频&#xff0c;MediaCodec直译媒体解码器&#xff0c;用于访问媒体编解码器&#xff0c;即编码器/解码器组件&#xff0c;它是 Android 多媒体支持基础设施的一部分&#xff1b;从广义上讲&#xff0c;编解码器处理输入数据以生成输出数据。它异…

OpenHarmony C/C++三方库移植适配

简介 众所周知&#xff0c;C/C三方库相对与JS/ETS的三方组件来说&#xff0c;其运行效率高。那如何将一个C/C三方库移植到OH系统上呢&#xff1f;本文将介绍如何快速高效的移植一个C/C三方库到OpenHarmony上。 C/C三方库适配问题与解决方案 由上图可以看出&#xff0c;三方库…

回溯算法初识

文章目录 回溯算法初识什么是回溯算法回溯算法的步骤回溯算模版例题 回溯算法初识 什么是回溯算法 ​ 回溯算法是一种通过不断尝试可能的解决方案来解决问题的算法。它通常用于解决组合优化问题&#xff0c;如排列组合问题、子集和问题等。该算法通过尝试所有可能的候选解&am…

【Redis】事务

Redis事务是一组命令的集合。这组命令顺序化执行而不会被其他命令插入。 Redis事务命令 命令描述DISCARD取消事务&#xff0c;放弃执行EXEC执行事务MULTI标记事务的开始UNWATCH取消WATCH对所有key的监控WATCH监控所有key Redis事务特点 特点说明单独的隔离操作Redis命令执行…

方法引用(详)

一.方法引用 1.概念&#xff1a;把已经有的方法拿过来用&#xff0c;当作函数式接口中抽象方法的方法体 2.使用方法引用之前需要满足的规则 &#xff08;1&#xff09;.引用处必须是函数式接口 &#xff08;2&#xff09;.被引用的方法必须已经存在&#xff08;如果不存在就…