spring框架实现滑动验证码功能

spring框架实现滑动验证码功能

  • 1. 整体描述
  • 2. 具体实现
    • 2.1 滑动验证码实体类
    • 2.2 滑动验证码登录VO
    • 2.3 滑动验证码接口返回类
    • 2.4 滑动验证码工具类
    • 2.5 滑动验证码Service
    • 2.6 滑动验证码Controller
  • 3 工程源码
  • 4 总结

1. 整体描述

之前项目需要在验证码模块,增加滑动验证码,用来给手机端使用的,大概看了下,主要方法就是将图片切割,然后记住偏移量,进行滑动,前端验证的时候,需要用前端传入的偏移量和生成的偏移量进行对比,如果在阈值之内,就验证通过,否则就不通过。具体实现方式见下文。之前没时间写,最近记录一下。

2. 具体实现

本工程主要依赖springboot框架,并且需要redis存验证码的信息,还需要几个图片,用来生成验证码。

2.1 滑动验证码实体类

package com.thcb.captchademo.captcha.domain;import lombok.Data;/*** 滑动验证码** @author thcb* @date 2023-05-25*/@Data
public class Captcha {/*** 随机字符串**/private String nonceStr;/*** 验证值**/private String value;/*** 生成的画布的base64**/private String canvasSrc;/*** 画布宽度**/private Integer canvasWidth;/*** 画布高度**/private Integer canvasHeight;/*** 生成的阻塞块的base64**/private String blockSrc;/*** 阻塞块宽度**/private Integer blockWidth;/*** 阻塞块高度**/private Integer blockHeight;/*** 阻塞块凸凹半径**/private Integer blockRadius;/*** 阻塞块的横轴坐标**/private Integer blockX;/*** 阻塞块的纵轴坐标**/private Integer blockY;/*** 图片获取位置**/private Integer place;
}

2.2 滑动验证码登录VO

package com.thcb.captchademo.captcha.domain;import lombok.Data;/*** 滑动验证码登录Vo** @author thcb* @date 2023-05-25*/@Data
public class LoginVo {/*** 随机字符串**/private String nonceStr;/*** 验证值**/private String value;
}

2.3 滑动验证码接口返回类

package com.thcb.captchademo.captcha.utils;import java.util.HashMap;/*** 操作消息提醒** @author thcb*/
public class AjaxResult extends HashMap<String, Object> {private static final long serialVersionUID = 1L;/*** 状态码*/public static final String CODE_TAG = "code";/*** 返回内容*/public static final String MSG_TAG = "msg";/*** 数据对象*/public static final String DATA_TAG = "data";/*** 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。*/public AjaxResult() {}/*** 初始化一个新创建的 AjaxResult 对象** @param code 状态码* @param msg  返回内容*/public AjaxResult(int code, String msg) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/*** 初始化一个新创建的 AjaxResult 对象** @param code 状态码* @param msg  返回内容* @param data 数据对象*/public AjaxResult(int code, String msg, Object data) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (data != null && !data.equals("")) {super.put(DATA_TAG, data);}}/*** 返回成功消息** @return 成功消息*/public static AjaxResult success() {return AjaxResult.success("操作成功");}/*** 返回成功数据** @return 成功消息*/public static AjaxResult success(Object data) {return AjaxResult.success("操作成功", data);}/*** 返回成功消息** @param msg 返回内容* @return 成功消息*/public static AjaxResult success(String msg) {return AjaxResult.success(msg, null);}/*** 返回成功消息** @param msg  返回内容* @param data 数据对象* @return 成功消息*/public static AjaxResult success(String msg, Object data) {return new AjaxResult(200, msg, data);}/*** 返回错误消息** @return*/public static AjaxResult error() {return AjaxResult.error("操作失败");}/*** 返回错误消息** @param msg 返回内容* @return 警告消息*/public static AjaxResult error(String msg) {return AjaxResult.error(msg, null);}/*** 返回错误消息** @param msg  返回内容* @param data 数据对象* @return 警告消息*/public static AjaxResult error(String msg, Object data) {return new AjaxResult(500, msg, data);}/*** 返回错误消息** @param code 状态码* @param msg  返回内容* @return 警告消息*/public static AjaxResult error(int code, String msg) {return new AjaxResult(code, msg, null);}
}

2.4 滑动验证码工具类

此类是核心类,主要实现了图片的切割功能。

package com.thcb.captchademo.captcha.utils;import com.thcb.captchademo.captcha.domain.Captcha;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.Objects;
import java.util.Random;/*** 滑动验证码工具类** @author thcb* @date 2023-05-25*/
public class CaptchaUtils {/*** 网络图片地址**/private final static String IMG_URL = "https://loyer.wang/view/ftp/wallpaper/%s.jpg";/*** 本地图片地址**/private final static String IMG_PATH = "E:\\caphcha\\%s.jpg";/*** 入参校验设置默认值**/public static void checkCaptcha(Captcha captcha) {//设置画布宽度默认值if (captcha.getCanvasWidth() == null) {captcha.setCanvasWidth(320);}//设置画布高度默认值if (captcha.getCanvasHeight() == null) {captcha.setCanvasHeight(155);}//设置阻塞块宽度默认值if (captcha.getBlockWidth() == null) {captcha.setBlockWidth(65);}//设置阻塞块高度默认值if (captcha.getBlockHeight() == null) {captcha.setBlockHeight(55);}//设置阻塞块凹凸半径默认值if (captcha.getBlockRadius() == null) {captcha.setBlockRadius(9);}//设置图片来源默认值if (captcha.getPlace() == null) {captcha.setPlace(1);}}/*** 获取指定范围内的随机数**/public static int getNonceByRange(int start, int end) {Random random = new Random();return random.nextInt(end - start + 1) + start;}/*** 获取验证码资源图**/public static BufferedImage getBufferedImage(Integer place) {try {//随机图片//获取网络资源图片if (0 == place) {int nonce = getNonceByRange(0, 1000);String imgUrl = String.format(IMG_URL, nonce);URL url = new URL(imgUrl);return ImageIO.read(url.openStream());}//获取本地图片else {int nonce = getNonceByRange(0, 20);String imgPath = String.format(IMG_PATH, nonce);File file = new File(imgPath);return ImageIO.read(file);}} catch (Exception e) {System.out.println("获取拼图资源失败");//异常处理return null;}}/*** 调整图片大小**/public static BufferedImage imageResize(BufferedImage bufferedImage, int width, int height) {Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);Graphics2D graphics2D = resultImage.createGraphics();graphics2D.drawImage(image, 0, 0, null);graphics2D.dispose();return resultImage;}/*** 抠图,并生成阻塞块**/public static void cutByTemplate(BufferedImage canvasImage, BufferedImage blockImage, int blockWidth, int blockHeight, int blockRadius, int blockX, int blockY) {BufferedImage waterImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);//阻塞块的轮廓图int[][] blockData = getBlockData(blockWidth, blockHeight, blockRadius);//创建阻塞块具体形状for (int i = 0; i < blockWidth; i++) {for (int j = 0; j < blockHeight; j++) {try {//原图中对应位置变色处理if (blockData[i][j] == 1) {//背景设置为黑色waterImage.setRGB(i, j, Color.BLACK.getRGB());blockImage.setRGB(i, j, canvasImage.getRGB(blockX + i, blockY + j));//轮廓设置为白色,取带像素和无像素的界点,判断该点是不是临界轮廓点if (blockData[i + 1][j] == 0 || blockData[i][j + 1] == 0 || blockData[i - 1][j] == 0 || blockData[i][j - 1] == 0) {blockImage.setRGB(i, j, Color.WHITE.getRGB());waterImage.setRGB(i, j, Color.WHITE.getRGB());}}//这里把背景设为透明else {blockImage.setRGB(i, j, Color.TRANSLUCENT);waterImage.setRGB(i, j, Color.TRANSLUCENT);}} catch (ArrayIndexOutOfBoundsException e) {//防止数组下标越界异常}}}//在画布上添加阻塞块水印addBlockWatermark(canvasImage, waterImage, blockX, blockY);}/*** 构建拼图轮廓轨迹**/private static int[][] getBlockData(int blockWidth, int blockHeight, int blockRadius) {int[][] data = new int[blockWidth][blockHeight];double po = Math.pow(blockRadius, 2);//随机生成两个圆的坐标,在4个方向上 随机找到2个方向添加凸/凹//凸/凹1Random random1 = new Random();int face1 = random1.nextInt(4);//凸/凹2int face2;//保证两个凸/凹不在同一位置do {Random random2 = new Random();face2 = random2.nextInt(4);} while (face1 == face2);//获取凸/凹起位置坐标int[] circle1 = getCircleCoords(face1, blockWidth, blockHeight, blockRadius);int[] circle2 = getCircleCoords(face2, blockWidth, blockHeight, blockRadius);//随机凸/凹类型int shape = getNonceByRange(0, 1);//圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆//计算需要的小图轮廓,用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色for (int i = 0; i < blockWidth; i++) {for (int j = 0; j < blockHeight; j++) {data[i][j] = 0;//创建中间的方形区域if ((i >= blockRadius && i <= blockWidth - blockRadius && j >= blockRadius && j <= blockHeight - blockRadius)) {data[i][j] = 1;}double d1 = Math.pow(i - Objects.requireNonNull(circle1)[0], 2) + Math.pow(j - circle1[1], 2);double d2 = Math.pow(i - Objects.requireNonNull(circle2)[0], 2) + Math.pow(j - circle2[1], 2);//创建两个凸/凹if (d1 <= po || d2 <= po) {data[i][j] = shape;}}}return data;}/*** 根据朝向获取圆心坐标*/private static int[] getCircleCoords(int face, int blockWidth, int blockHeight, int blockRadius) {//上if (0 == face) {return new int[]{blockWidth / 2 - 1, blockRadius};}//左else if (1 == face) {return new int[]{blockRadius, blockHeight / 2 - 1};}//下else if (2 == face) {return new int[]{blockWidth / 2 - 1, blockHeight - blockRadius - 1};}//右else if (3 == face) {return new int[]{blockWidth - blockRadius - 1, blockHeight / 2 - 1};}return null;}/*** 在画布上添加阻塞块水印*/private static void addBlockWatermark(BufferedImage canvasImage, BufferedImage blockImage, int x, int y) {Graphics2D graphics2D = canvasImage.createGraphics();graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.8f));graphics2D.drawImage(blockImage, x, y, null);graphics2D.dispose();}/*** BufferedImage转BASE64*/public static String toBase64(BufferedImage bufferedImage, String type) {try {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ImageIO.write(bufferedImage, type, byteArrayOutputStream);String base64 = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());return String.format("data:image/%s;base64,%s", type, base64);} catch (IOException e) {System.out.println("图片资源转换BASE64失败");//异常处理return null;}}
}

2.5 滑动验证码Service

service层,,封装了工具类的一些方法,这里为了简单没写接口,正常应该是service和impl两个类。

package com.thcb.captchademo.captcha.service;import com.thcb.captchademo.captcha.domain.Captcha;
import com.thcb.captchademo.captcha.utils.CaptchaUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;import java.awt.image.BufferedImage;
import java.util.UUID;
import java.util.concurrent.TimeUnit;/*** 滑动验证码Service** @author thcb* @date 2023-05-25*/@Service
public class CaptchaService {/*** 拼图验证码允许偏差**/private static Integer ALLOW_DEVIATION = 3;@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 校验验证码** @param imageKey* @param imageCode* @return boolean**/public String checkImageCode(String imageKey, String imageCode) {ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();String key = "imageCode:" + imageKey;String text = ops.get(key);if (text == null || text.equals("")) {return "验证码已失效";}// 根据移动距离判断验证是否成功if (Math.abs(Integer.parseInt(text) - Integer.parseInt(imageCode)) > ALLOW_DEVIATION) {return "验证失败,请控制拼图对齐缺口";}// 验证成功,删除redis缓存stringRedisTemplate.delete(key);return null;}/*** 缓存验证码,有效期1分钟** @param key* @param code**/public void saveImageCode(String key, String code) {ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();ops.set("imageCode:" + key, code, 60, TimeUnit.SECONDS);}/*** 获取验证码拼图(生成的抠图和带抠图阴影的大图及抠图坐标)**/public Object getCaptcha(Captcha captcha) {//参数校验CaptchaUtils.checkCaptcha(captcha);//获取画布的宽高int canvasWidth = captcha.getCanvasWidth();int canvasHeight = captcha.getCanvasHeight();//获取阻塞块的宽高/半径int blockWidth = captcha.getBlockWidth();int blockHeight = captcha.getBlockHeight();int blockRadius = captcha.getBlockRadius();//获取资源图BufferedImage canvasImage = CaptchaUtils.getBufferedImage(captcha.getPlace());//调整原图到指定大小canvasImage = CaptchaUtils.imageResize(canvasImage, canvasWidth, canvasHeight);//随机生成阻塞块坐标int blockX = CaptchaUtils.getNonceByRange(blockWidth, canvasWidth - blockWidth - 10);int blockY = CaptchaUtils.getNonceByRange(10, canvasHeight - blockHeight + 1);//阻塞块BufferedImage blockImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);//新建的图像根据轮廓图颜色赋值,源图生成遮罩CaptchaUtils.cutByTemplate(canvasImage, blockImage, blockWidth, blockHeight, blockRadius, blockX, blockY);// 移动横坐标String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");// 缓存saveImageCode(nonceStr, String.valueOf(blockX));//设置返回参数captcha.setNonceStr(nonceStr);captcha.setBlockY(blockY);captcha.setBlockSrc(CaptchaUtils.toBase64(blockImage, "png"));captcha.setCanvasSrc(CaptchaUtils.toBase64(canvasImage, "png"));return captcha;}
}

2.6 滑动验证码Controller

controller层有两个方法,一个是获取验证码的方法,一个是验证码校验的方法。

package com.thcb.captchademo.captcha.controller;import com.thcb.captchademo.captcha.domain.Captcha;
import com.thcb.captchademo.captcha.domain.LoginVo;
import com.thcb.captchademo.captcha.service.CaptchaService;
import com.thcb.captchademo.captcha.utils.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 滑动验证码Controller** @author thcb* @date 2023-05-25*/@RestController
@RequestMapping("/captcha")
public class CaptchaController {@Autowiredprivate CaptchaService captchaService;@PostMapping("/slideCaptchaImage")public AjaxResult getCaptcha() {return AjaxResult.success(captchaService.getCaptcha(new Captcha()));}@PostMapping(value = "/login")public AjaxResult login(@RequestBody LoginVo loginVo) {// 验证码校验String msg = captchaService.checkImageCode(loginVo.getNonceStr(), loginVo.getValue());if (msg != null && !msg.equals("")) {return AjaxResult.error(msg);}return AjaxResult.success();}}

3 工程源码

工程不算复杂,源码上传到GitCode,项目地址,上传有点问题,而且目前只能设置成私密项目,等之后可以调整的时候再改成公开的。

4 总结

滑动验证码功能不算复杂,可以和项目当前已有的验证码共存,调用不同的接口,返回不同类型的验证码,当然这个就根据项目具体情况确定了。

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

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

相关文章

Android中接入hook框架:lancet-base

me.ele:lancet-base是"饿了么"开发的Android平台的开源hook框架&#xff0c;GitHub地址为&#xff1a;https://github.com/eleme/lancet。 此框架的优点如下&#xff1a; 1.编译速度快, 并且支持增量编译。 2.简洁的 API, 几行 Java 代码完成注入需求。 3.没有任何多…

Dataview的初次学习

一个很简单的例子 list from "" where contains(file.name,"教程")详细介绍 目的不是花哨&#xff0c;而是精简的整合自己的笔记&#xff0c;方便查找和翻阅。 代码块开头都省略了dataview提示词 我参考的教程&#xff1a; obsidian插件之dataview入门…

MongoDB教程 :MongoDB全文检索

MongoDB Full-Text Search Tutorial MongoDB provides a robust full-text search functionality that allows for efficient and powerful text searching capabilities. Here’s a comprehensive guide on how to utilize MongoDB’s full-text search. 1. Setting Up Mong…

【日常记录-JS】获取URL参数

Author&#xff1a;赵志乾 Date&#xff1a;2024-07-24 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 简介 实际项目中经常有些落地页会携带参数&#xff0c;并要求在发起请求时将携带的参数一并传递至后台服务。例如两个独立系统A和B&…

Linux系统上安装zookeeper

百度网盘 通过网盘分享的文件&#xff1a;zookeeper_linux 链接: https://pan.baidu.com/s/1_hybXZVwTRkotz0VbwbSMw?pwd8888 提取码: 8888 1.将压缩包拖进虚拟机 2.解压压缩包 cd /ruanjian/zookeeper/ tar -zxvf apache-ZooKeeper-3.7.2-bin.tar.gz3. 进入到conf目录 cd …

《python程序语言设计》第6章12题 显示字符,使用下面的函数头,编写一个打印字符的函数

def printChars(ch1, ch2, numberPerLine):a ord(ch1)b ord(ch2)count 0for i in range(a, b 1):count 1print(chr(i), end" ")if count % numberPerLine 0:print()printChars("1", "Z", 10)

以FastGPT为例提升Rag知识库应用中的检索召回命中率

提升Rag知识库应用中的检索召回命中率 在构建Rag&#xff08;Retrieval-Augmented Generation&#xff09;知识库应用时&#xff0c;检索召回知识片段的命中率是至关重要的。高效、准确的检索机制是确保AI系统能够精准响应用户查询的基础。当前&#xff0c;FastGPT主要采用三种…

使用python中的特殊字典——defaultdict

专栏总目录 一、defaultdict说明 在Python中是一个特殊类型的字典&#xff0c;它是collections模块中的一个类defaultdict的实例。这个字典与普通的字典dict不同之处在于&#xff0c;当你试图访问一个不存在的键时&#xff0c;defaultdict会自动创建一个新条目&#xff0c;其值…

使用SpringBoot集成Kafka实现用户数据变更后发送消息

概述 当使用Spring Boot集成Kafka实现用户数据变更后&#xff0c;向其他厂商发送消息&#xff0c;我们需要考虑以下步骤&#xff1a;配置Kafka连接、创建Kafka Producer发送消息、监听用户数据变更事件&#xff0c;并将事件转发到Kafka。 1. 环境准备 确保已经安装Java开发环…

【java基础】java中配置文件格式以及读取方式

在Java中&#xff0c;配置文件可以采用多种格式&#xff0c;每种格式都有其特定的使用场景和优势。以下是一些常见的配置文件格式以及如何在Java中读取它们的方法&#xff1a; 1. Properties 文件 (.properties) Properties 文件是一种常见的配置文件格式&#xff0c;它使用键…

C++沉思:预处理和编译

预处理和编译 条件编译源代码使用方式典型示例原理 使用static_assert执行编译时断言检查使用方式原理 在C中&#xff0c;编译是将源代码转换为机器代码并组织在目标文件中&#xff0c;然后将目标文件链接在一起生成可执行文件的过程。编译器实际上一次只处理一个文件&#xff…

Oracle核心进程详解并kill验证

Oracle核心进程详解并kill验证 文章目录 Oracle核心进程详解并kill验证一、说明二、核心进程详解2.1.PMON-进程监控进程2.2.SMON-系统监控进程2.3.DBWn-数据库块写入进程2.4. LGWR-日志写入器进程2.5. CKPT-检查点进程 三、Kill验证3.1.kill ckpt进程3.2.kill pmon进程3.3.kill…

智慧工地视频汇聚管理平台:打造现代化工程管理的全新视界

一、方案背景 科技高速发展的今天&#xff0c;工地施工已发生翻天覆地的变化&#xff0c;传统工地管理模式很容易造成工地管理混乱、安全事故、数据延迟等问题&#xff0c;人力资源的不足也进一步加剧了监管不到位的局面&#xff0c;严重影响了施工进度质量和安全。 视频监控…

中小企业数字化转型的关键五步,你了解吗?

在信息技术迅猛发展的当下&#xff0c;数字化转型已成为中小企业提升竞争力、实现可持续发展的关键策略。在数字化转型过程中&#xff0c;工业软件作为贯穿生产全流程的智能化引擎&#xff0c;其选择与应用显得尤为关键。那么&#xff0c;中小企业应如何科学合理的规划数字化转…

Vue前端页面嵌入mermaid图表--流程图

一、安装Mermaid 首先&#xff0c;你需要在你的项目中安装Mermaid。可以通过npm或yarn来安装&#xff1a; npm install mermaid --save # 或者 yarn add mermaid结果如图&#xff1a; 二、Vue 方法一&#xff1a;使用pre标签 使用ref属性可以帮助你在Vue组件中访问DOM元素 …

对于接口调用方式,可以使用两种不同的技术:Web Service 和 Dubbo。下面我将简要解释它们以及如何在 Maven 项目中集成它们。

对于接口调用方式&#xff0c;可以使用两种不同的技术&#xff1a;Web Service 和 Dubbo。下面我将简要解释它们以及如何在 Maven 项目中集成它们。 ### 1. Web Service&#xff08;WS&#xff09; Web Service 是一种基于标准化协议和格式进行通信的技术&#xff0c;允许不同…

数据结构 | LinkedList与链表

前言 ArrayList底层使用连续的空间,任意位置(尤其是0位置下标)插入或删除元素时,需要将该位置后序元素 整体 往前或往后搬移,故时间复杂度为O(N). 优点(给定一个下标,可以快速查找到对应的元素,时间复杂度为O(1))增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗.增容一…

【权威发布】第二届雷达、信号与信息处理国际会议(RSIP 2024)

第二届雷达、信号与信息处理国际会议 2024 International Conference on Radar, Signal and Information Processing 【1】会议简介 第二届雷达、信号与信息处理国际会议是一次聚焦雷达技术、信号处理及信息处理领域最新研究成果和前沿趋势的盛会。会议旨在汇聚国内外雷达与信号…

【Django】anaconda环境变量配置及配置python虚拟环境

文章目录 配置环境变量配置python虚拟环境查看conda源并配置国内源在虚拟环境中安装django 配置环境变量 control sysdm.cpl,,3笔者anaconda安装目录为C:\ProgramData\anaconda3 那么需要加入path中的有如下三个 C:\ProgramData\anaconda3 C:\ProgramData\anaconda3\Scripts C:…

【C++】类和对象(三)完结篇

个人主页 创作不易&#xff0c;感谢大家的关注&#xff01; 文章目录 ⭐一、再探构造函数1.初始化列表 &#x1f389;二、类型转换&#x1f3e0;三、static成员&#x1f3dd;️四、友元⏱️五、内部类&#x1f388;六、匿名对象&#x1f3a1;七、在拷贝对象时的编译器优化 ⭐一…