AES加密,全平台实现!

news2024/10/11 23:21:24

前言

今天我们来聊聊数据加密与隐私相关话题。本人开发的加密工具,欢迎体验!https://www.pgyer.com/cryptotools 。AES加密,通俗的话来讲,就是用一个key把原数据变成一个新数据,也通过这个key还原成原数据。所以,它是一种对称的加密方式。只要别人不知道这个key,就无法解开数据的内容。它是DES加密的升级版本,由于AES的密钥长度更长,所以更安全。但是它的效率略有降低,不过这对于现代的计算机设备来说可以忽略不计。数字加密在当今数字时代的重要性不容忽视。

数字加密的重要性

数字加密的重要性,有以下几个主要方面:

  1. 数据保护:加密技术将信息转化为无法读取的格式,只有授权方可以解密。这是防止数据泄露的第一道防线,尤其在传输敏感信息(如个人身份信息、金融数据等)时尤为重要。
  2. 隐私保障:加密技术能够保护用户的隐私,确保通信双方之外的任何人无法窃听或截取对话内容。这在个人通信、电子邮件、社交媒体等领域非常关键。
  3. 安全通信:在互联网的应用中,许多协议(如HTTPS)依赖于加密来确保数据传输的安全性,防止中间人攻击和数据篡改。
  4. 身份验证:数字加密还用于验证身份,例如数字签名,确保发件人或设备的真实身份,防止身份冒用。
  5. 保护区块链和数字货币:加密技术是区块链和加密货币(如比特币)的核心,它确保了交易的安全性、不可篡改性和用户的匿名性。
  6. 防止数据篡改:加密不仅保护数据的机密性,还能通过散列函数(如SHA-256)等加密算法检测数据是否被篡改。这在文件验证、软件更新等场景中非常有用。

通过这些方式,数字加密在保障网络安全、数据隐私以及构建安全数字基础设施中起到了至关重要的作用。

AES加密介绍

AES(Advanced Encryption Standard,高级加密标准)是一种对称加密算法,用于加密和解密数据。AES 由美国国家标准与技术研究院(NIST)于2001年发布,成为取代DES(数据加密标准)的新加密标准。它广泛应用于政府、企业和个人数据保护中,特别是在金融、通信和网络安全领域。

AES的主要特点
  1. 对称加密

    • 同一密钥:AES是一种对称加密算法,这意味着加密和解密都使用相同的密钥。也就是说,发件人和接收人必须共享同一个密钥才能进行加密和解密。
  2. 加密块大小

    • 固定的块大小:AES处理的数据块大小是128位(16字节)。如果明文长度不足128位,AES使用填充方式(如PKCS7)补齐。
  3. 密钥长度

    • AES支持三种不同的密钥长度:128位、192位和256位。

      • AES-128:使用128位密钥,安全性高且速度较快。
      • AES-192:使用192位密钥。
      • AES-256:使用256位密钥,提供最高的安全性,但速度稍慢。
  4. 加密模式: AES加密常与不同的操作模式结合使用,常见的模式有:

    • ECB模式(电子密码本模式):每个块独立加密,不推荐使用,容易导致模式被破解。
    • CBC模式(密码分组链接模式):每个块的加密依赖于前一个块,常用于网络通信,确保数据的随机性。
    • CFB、OFB模式:这些模式常用于流加密或特殊场景。
  5. 安全性

    • AES算法基于复杂的代数结构,当前没有已知的高效攻击方法,即使使用现代计算机也无法在合理时间内破解。AES-256被认为是最安全的算法之一,适用于高安全性需求场景。
AES的应用
  • 数据加密:用于加密文件、数据库和存储设备,以保护敏感信息不被泄露。
  • 网络通信:广泛应用于TLS/SSL等加密协议,确保互联网通信的安全性(如HTTPS)。
  • 无线通信:Wi-Fi安全协议(如WPA2)使用AES加密,以确保无线网络传输数据的安全。
  • 区块链:加密货币和区块链技术中,也使用AES确保钱包和交易数据的安全。

AES加密因其高效、安全和标准化而成为当今广泛使用的加密技术,是保护个人隐私、企业数据和国家安全的基石。

AES加密中的重要概念

初始化向量(偏移量) / IV
加密方式
电子密码本 / ECB

ECB不需要偏移量iv

密码块连接 / CBC

在CBC中,每个明文块要先与前一个密文块进行异或后再加密,每个密文块都依赖于前面的所有明文块。

密文反馈 / CFB

CFB的加密跟解密过程几乎完全相同,注意它在解密过程中使用的是AES加密而不是AES解密。

输出反馈 / OFB

这个很简单,跟CFB128很相似,不同的是它是直接把输出块作为下一个块加密的输入块。

计数器模式 / CTR

COUNTER是整个CTR模式的核心所在。它是由IV经过一定的规则之后生成的一段数据,长度与数据块的长度相等。接着我们要选定一个数m,这个m是用于确定计数器中累加部分的大小的,通常取块大小的一半,块大小是奇数就四舍五入(当然对于AES并没有这个问题)。初始的计数器COUNTER1长度固定的任意一个随机字节序列,而不是像想象中那样一段随机数后面跟着一段0。

填充
NoPadding

顾名思义,就是不填充。缺点就是只能加密长为128bits倍数的信息,一般不会使用。

ZeroPadding

全部填充0x00,无论缺多少全部填充0x00,已经是128bits倍数仍要填充。

… | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |

PKCS5和PKCS7

对于AES来说PKCS5Padding和PKCS7Padding是完全一样的,不同在于PKCS5限定了块大小为8bytes而PKCS7没有限定。因此对于AES来说两者完全相同,但是对于Rijndael就不一样了。AES是Rijndael在块大小为8bytes时的特例,对于使用其他信息块大小的Rijndael算法只能使用PKCS7。

在AES加密当中严格来说是不能使用pkcs5的,因为AES的块大小是16bytes而pkcs5只能用于8bytes,通常我们在AES加密中所说的pkcs5指的就是pkcs7。

… | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |

ISO 10126

最后一个字节是填充的字节数(包括最后一字节),其他全部填随机数

… | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 |

ANSI X9.23

跟ISO 10126很像,只不过ANSI X9.23其他字节填的都是0而不是随机数

… | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04

AES加密的代码实现

Android&Java
/**
 * AES encryption.
 * 简体中文:AES加密。
 *
 * @param secretKey Key
 * @param transformation In the field of encryption, it usually refers to the combination
 *                       of encryption algorithms, modes, and padding.
 * @param iv Offset
 * @param data Data to be encrypted
 */
public static String encryptAES(String secretKey, String transformation, IvParameterSpec iv, String data) {
    try {
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(secretKey, AES), iv);
        byte[] encryptByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return base64Encode(encryptByte);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Encrypting with AES, using the default mode.
 * 简体中文:AES加密,使用默认的方式。
 *
 * @param secretKey  Key
 * @param data Data to be encrypted
 */
public static String encryptAES(String secretKey, String data) {
    try {
        Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5);
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(secretKey, AES));
        byte[] encryptByte = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return base64Encode(encryptByte);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Decrypting with AES.
 * 简体中文:AES解密。
 *
 * @param secretKey  Key
 * @param transformation In the field of encryption, it usually refers to the combination
 *                       of encryption algorithms, modes, and padding.
 * @param iv Offset
 * @param base64Data Base64 data to be decrypted
 */
public static String decryptAES(String secretKey, String transformation, IvParameterSpec iv, String base64Data) {
    try {
        byte[] data = base64Decode(base64Data);
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.DECRYPT_MODE, getSecretKey(secretKey, AES), iv);
        byte[] result = cipher.doFinal(data);
        return new String(result, StandardCharsets.UTF_8);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Decrypting using AES, using the default mode.
 * 简体中文:AES解密,使用默认的方式。
 *
 * @param secretKey  Key
 * @param base64Data Base64 data to be decrypted
 */
public static String decryptAES(String secretKey, String base64Data) {
    try {
        byte[] data = base64Decode(base64Data);
        Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5);
        cipher.init(Cipher.DECRYPT_MODE, getSecretKey(secretKey, AES));
        byte[] result = cipher.doFinal(data);
        return new String(result, StandardCharsets.UTF_8);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Encrypting a file using AES.
 * 简体中文:对文件进行AES加密。
 *
 * @param srcFile Source encrypted file
 * @param dir     Storage path of the encrypted file
 * @param dstName Encrypted file name
 * @param secretKey  Key
 * @param transformation In the field of encryption, it usually refers to the combination
 *                       of encryption algorithms, modes, and padding.
 * @return Encrypted file
 */
public static File encryptFileAES(File srcFile, String dir, String dstName, String secretKey, String transformation) {
    try {
        File encryptFile = new File(dir, dstName);
        FileOutputStream outputStream = new FileOutputStream(encryptFile);
        Cipher cipher = initFileAESCipher(secretKey, transformation, Cipher.ENCRYPT_MODE);
        CipherInputStream cipherInputStream = new CipherInputStream(
                new FileInputStream(srcFile), cipher);
        byte[] buffer = new byte[1024 * 2];
        int len;
        while ((len = cipherInputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, len);
            outputStream.flush();
        }
        cipherInputStream.close();
        IoUtils.close(outputStream);
        return encryptFile;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Encrypting a file using AES, with the default mode.
 * 简体中文:AES加密文件,默认方式。
 *
 * @param srcFile Source encrypted file
 * @param dir     Storage path of the encrypted file
 * @param dstName Encrypted file name
 * @param secretKey  Key
 */
public static File encryptFileAES(File srcFile, String dir, String dstName, String secretKey) {
    return encryptFileAES(srcFile, dir, dstName, secretKey, AES_CFB_PKCS5);
}

/**
 * Encrypting a file using AES.
 * 简体中文:AES解密文件。
 *
 * @param srcFile Source encrypted file
 * @param dir        Storage path of the decrypted file
 * @param dstName Decrypted file name
 * @param secretKey  Key
 * @param transformation In the field of encryption, it usually refers to the combination
 *                       of encryption algorithms, modes, and padding.
 */
public static File decryptFileAES(File srcFile, String dir, String dstName, String secretKey, String transformation) {
    try {
        File decryptFile = new File(dir, dstName);
        Cipher cipher = initFileAESCipher(secretKey, transformation, Cipher.DECRYPT_MODE);
        FileInputStream inputStream = new FileInputStream(srcFile);
        CipherOutputStream cipherOutputStream = new CipherOutputStream(
                new FileOutputStream(decryptFile), cipher);
        byte[] buffer = new byte[1024 * 2];
        int len;
        while ((len = inputStream.read(buffer)) >= 0) {
            cipherOutputStream.write(buffer, 0, len);
            cipherOutputStream.flush();
        }
        IoUtils.close(cipherOutputStream, inputStream);
        return decryptFile;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Decrypting a file using AES, with the default mode.
 * 简体中文:AES解密文件,默认方式。
 *
 * @param srcFile Source encrypted file
 * @param dir        Storage path of the decrypted file
 * @param dstName Decrypted file name
 * @param secretKey  Key
 */
public static File decryptFileAES(File srcFile, String dir, String dstName, String secretKey) {
    return decryptFileAES(srcFile, dir, dstName, secretKey, AES_CFB_PKCS5);
}

/**
 * Initialize AES Cipher.
 * 简体中文:初始化AES Cipher。
 *
 * @param secretKey  Key
 * @param transformation In the field of encryption, it usually refers to the combination
 *                       of encryption algorithms, modes, and padding.
 * @param cipherMode Encryption mode
 * @return Cryptographic Algorithm
 */
private static Cipher initFileAESCipher(String secretKey, String transformation, int cipherMode) {
    try {
        SecretKeySpec secretKeySpec = getSecretKey(secretKey, AES);
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(cipherMode, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        return cipher;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Java的AES加解密是JDK的内置模块,以上是我Java代码的封装,支持加密文本和文件,工具类在https://github.com/dora4/dora 也可以找到。

iOS
#import <CommonCrypto/CommonCryptor.h>
#import <Foundation/Foundation.h>

@interface AESCrypto : NSObject

+ (NSData *)AES256EncryptWithKey:(NSString *)key data:(NSData *)data;
+ (NSData *)AES256DecryptWithKey:(NSString *)key data:(NSData *)data;

@end

@implementation AESCrypto

+ (NSData *)AES256EncryptWithKey:(NSString *)key data:(NSData *)data {
    return [self AES256Operation:kCCEncrypt key:key data:data];
}

+ (NSData *)AES256DecryptWithKey:(NSString *)key data:(NSData *)data {
    return [self AES256Operation:kCCDecrypt key:key data:data];
}

+ (NSData *)AES256Operation:(CCOperation)operation key:(NSString *)key data:(NSData *)data {
    char keyPtr[kCCKeySizeAES256+1]; // 密钥长度(256位 = 32字节)
    bzero(keyPtr, sizeof(keyPtr)); // 初始化keyPtr
    
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; // 将密钥转换为C字符串

    size_t dataOutAvailable = data.length + kCCBlockSizeAES128; // 输出缓冲区大小
    void *dataOut = malloc(dataOutAvailable); // 分配内存
    size_t dataOutMoved = 0; // 实际加密数据的大小

    // 执行AES加密/解密
    CCCryptorStatus status = CCCrypt(operation,                  // 加密或解密操作
                                     kCCAlgorithmAES,             // 加密算法
                                     kCCOptionPKCS7Padding,       // 填充方式
                                     keyPtr,                      // 密钥
                                     kCCKeySizeAES256,            // 密钥长度
                                     NULL,                        // 初始向量(此处为nil)
                                     data.bytes,                  // 输入数据
                                     data.length,                 // 输入数据长度
                                     dataOut,                     // 输出缓冲区
                                     dataOutAvailable,            // 输出缓冲区大小
                                     &dataOutMoved);              // 输出大小

    if (status == kCCSuccess) {
        // 成功,返回加密后的数据
        return [NSData dataWithBytesNoCopy:dataOut length:dataOutMoved];
    }

    // 失败,释放内存并返回nil
    free(dataOut);
    return nil;
}

@end

iOS一般使用CommonCrypto进行AES的加解密。我们同样也定义成工具类,以AES256为例,定义两个类方法AES256EncryptWithKey和AES256DecryptWithKey。底层最终调用CCCrypt来实现加解密。

前端
import CryptoJS from "crypto-js";

static encryptAES(plainText) {
    const key = "12345678abcdefgh";
    const iv = '0000000000000000';
    const encodedContent = CryptoJS.enc.Utf8.parse(plainText);
    const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
    const ivUtf8 = CryptoJS.enc.Utf8.parse(iv);
    const encrypted = CryptoJS.AES.encrypt(encodedContent, keyUtf8, {
        iv: ivUtf8,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    const cipherText = encrypted.toString();
    console.log(cipherText);
    return cipherText;
}

static decryptAES(cipherText) { 
    const key = "12345678abcdefgh"; 
    const iv = '0000000000000000'; 
    const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
    const ivUtf8 = CryptoJS.enc.Utf8.parse(iv);
    const decrypted = CryptoJS.AES.decrypt(cipherText, keyUtf8, {
            iv: ivUtf8, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
    });
    const decryptedText = decrypted.toString(CryptoJS.enc.Utf8); 
    console.log(decryptedText);
    return decryptedText;
}

前端通常使用CryptoJS库进行AES的加解密。人家都给你封装好了,直接调用即可。

注意事项

由于各个版本库的加密实现方式可能会不一样,所以加密出来的结果一般不会一样,但是解密出来都是相同的内容。如果你要加密出来也要一样,那我建议你用底层语言C/C++去写,然后打成动态链接库,供上层语言调用。或者使用诸如Flutter等跨平台/混合开发技术。另外推荐一个网页版的加解密工具 http://tool.chacuo.net/cryptaes ,支持的加解密方式贼多。

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

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

相关文章

Linux的kafka安装部署

1.kafka是一个分布式的,去中心化的,高吞吐低延迟,订阅模式的消息队列系统 确保要有jdk与zookeeper安装配置 2.下载kafka安装包 http://archive.apache.org/dist/kafka/2.4.1/kafka_2.12-2.4.1.tgz 此时可以wget http://archive.apache.org/dist/kafka/2.4.1/kafka_2.12-2.4.…

数字后端零基础入门系列 | Innovus零基础LAB学习Day1

一 Floorplan 数字IC后端设计如何从零基础快速入门&#xff1f;(内附数字IC后端学习视频&#xff09; Lab5-1这个lab学习目标很明确——启动Innovus工具并完成设计的导入。 在进入lab之前&#xff0c;我们需要进入我们的FPR工作目录。 其中ic062为个人服务器账户。比如你端…

Graph Contrastive Learningwith Reinforcement Augmentation

IJCAI24 推荐指数&#xff1a; #paper/⭐⭐⭐ 领域&#xff1a;图增强强化学习 胡言乱语&#xff1a; 不愧是清华组的论文&#xff0c;这个实验的目的是利用强化学习去生成对比学习的增强视图。但是&#xff0c;其仍然有一些小问题&#xff1a;其本质实际是对以往的图增强方法…

vrrp实验

配置Trunk和Access [SW3]int e0/0/1 [SW3-Ethernet0/0/1]p l a [SW3-Ethernet0/0/1]p d v 10 [SW3-Ethernet0/0/1]int e0/0/2 [SW3-Ethernet0/0/2]p l a [SW3-Ethernet0/0/2]p d v 10 [SW3-Ethernet0/0/2]int e0/0/3 [SW3-Ethernet0/0/3]p l a [SW3-Ethernet0/0/3]p d v 20 [S…

postgresql 安装

一、下载 PostgreSQL: File Browser 下载地址 PostgreSQL: File Browser 上传到服务器,并解压 二、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel 创建postgresql 和目录 useradd …

位运算 -- 力扣

1486. 数组异或操作 1486. 数组异或操作 根据题意&#xff0c;使用参数 n 和 start 生成一个数组&#xff0c;最后返回数组中所有元素按位异或&#xff08;XOR&#xff09;后得到的结果。 首先&#xff0c;异或运算的规则是&#xff0c;当同一位的二进制数不同时结果为 1&…

【Golang】Go 语言中的 time 包详解:全面掌握时间处理与应用

在 Go 语言中&#xff0c;time 包提供了强大的时间处理功能&#xff0c;适用于各种场景&#xff1a;获取当前时间、格式化和解析时间、计算时间间隔、设置定时器、处理超时等。在开发过程中&#xff0c;熟练掌握 time 包能够帮助我们轻松处理时间相关的操作&#xff0c;尤其是定…

小程序之获取用户头像与昵称

html 这个是通过一个点击事件进行显示的,下面是效果图,获取头像最关键的是 open-type"chooseAvatar" ,获取昵称最关键的是 type"nickname" ,大家多加注意i哦 <!-- 大的输入调取 --><view class"myis_box" wx:if"{{showMyisBox}}…

做ppt用什么软件好?5个办公必备的ppt工具推荐!

ppt用什么软件做&#xff1f; 相信很多人看到这个&#xff0c;会第一时间想到Microsoft Office套件包含的幻灯片软件Powerpoint&#xff0c;它的名声甚广&#xff0c;以至于某种程度上成了PPT的代名词。 在Powerpoint之外&#xff0c;这些年也陆续诞生了各式各样的PPT软件&am…

CocosCreator 快速部署 TON 游戏:Web2 游戏如何使用 Ton支付

在本篇文章中&#xff0c;我们将继续探讨如何使用 Cocos Creator 开发 Telegram 游戏&#xff0c;重点介绍如何集成 TON 支付功能。通过这一教程&#xff0c;开发者将学会如何在游戏中接入 TON Connect&#xff0c;实现钱包连接、支付以及支付后的校验流程&#xff0c;最终为 W…

YOLO11改进|SPPF篇|引入SPPFCSPC金字塔结构

目录 一、【SPPFCSPC】金字塔结构1.1【SPPFCSPC】金字塔结构介绍1.2【SPPFCSPC】核心代码 二、添加【SPPFCSPC】金字塔结构2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【SPPFCSPC】金字塔结构 1.1【SPPFCSPC】金字塔结构介绍 下图是…

vue后台管理系统从0到1(1)

文章目录 vue后台管理系统从0到1&#xff08;1&#xff09;nvm 下载安装1.卸载nodejs环境2.安装nvm 安装nrm vue后台管理系统从0到1&#xff08;1&#xff09; 第一节主要是先安装我们的工具nvm nodejs版本管理工具&#xff0c;和nrm镜像管理工具 nvm 下载安装 nvm是一款管理…

网络流量预测的学习——持续更新ing

文章目录 前情提要何为网络流量网络流量分析&#xff08;NTA&#xff09;网络流量组成网络流量处理过程 预测网络流量的工具wiresharkbrim&#xff08;zui&#xff09; 机器学习中的网络流量预测参考文章 前情提要 记录一些有关网络流量的学习 何为网络流量 网络流量是指在计…

【D3.js in Action 3 精译_033】4.1.0 DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

又被特斯拉演了?继续“画饼式”发布Robotaxi,产业链静观其变

9月底的暴涨后&#xff0c;A股资产正经历回调&#xff0c;科技板块变现参差。不过&#xff0c;无人驾驶领域的预期依然很强。 10月10日科技股全线调整之际&#xff0c;无人驾驶板块盘中的巨幅震荡拉升就是典型的预热动作。东箭科技、天龙股份等多只智能驾驶个股涨停。核心驱动…

ACR、PZ、AMC仪表接线说明及通讯协议解析

1.ACR/PZ/AMC多功能表接线说明 三相三线接线说明 使用场合负载是平衡系统&#xff0c;并且没有零线的场合。 1. 端子号1&#xff0c;2为辅助电源&#xff1a; 如上图&#xff0c;接入相电压220V输入。其中辅助电源的火线加装5A保险丝&#xff0c;零线直接接到零排上。 2&am…

iPhone使用指南:如何在没有备份的情况下从 iPhone 恢复已删除的照片

本指南将向您展示如何在没有备份的情况下从 iPhone 恢复已删除的照片。我们所有人在生活中的某个时刻都一定做过一些愚蠢的事情&#xff0c;例如从手机或电脑中删除一些重要的东西。这是很自然的&#xff0c;没有什么可羞耻的。您可能在辛苦工作一天后回来。当突然想看一些照片…

C++开发五子棋游戏案例详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

JavaSE——集合3:ArrayList、Vector

目录 一、ArrayList的注意事项 二、ArrayList的扩容机制(重要) 三、Vector底层结构和源码剖析 1.Vector类的定义说明 2.Vector底层也是一个对象数组 3.Vector是线程同步的&#xff0c;即线程安全&#xff0c;Vector类的操作方法带有synchronized 4.在开发中&#xff0c…

obs录屏怎么样?四大优秀录屏工具亲测好用!

录屏需求日盛&#xff0c;接下来我们就来聊聊几款市面上较为热门的录屏软件——福昕录屏大师、转转大师录屏、爱拍录屏以及经典的obs录屏&#xff0c;希望能给寻找合适录屏工具的您带来一些灵感。 福昕录屏大师 直达链接&#xff1a;www.foxitsoftware.cn/REC/ 如果你刚开始…