数据加密标准(DES)解析及代码实现(java)

article/2025/6/7 14:58:57

概述

数据加密标准(Data Encryption Standard, DES)是1977年由美国国家标准局(NIST)采纳的对称密钥加密算法,作为首个公开的联邦信息处理标准(FIPS PUB 46)。DES采用64位分组大小和56位有效密钥长度(外加8位奇偶校验位),通过16轮Feistel网络结构实现数据加密。尽管已被AES取代,DES仍是理解现代密码学的基石。

历史意义

  • 首个公开的加密标准(1977-2005)
  • 推动了密码学研究的发展
  • 启发了3DES、AES等后续算法
  • 证明了Feistel网络的实际可行性

核心结构与特点

1. Feistel网络结构

1. 64位明文输入
2. 初始置换(IP)
3. 分割为L0(左32位)和R0(右32位)
4. 16轮迭代处理:- 每轮:R_i = L_{i-1} ⊕ F(R_{i-1}, K_i)-        L_i = R_{i-1}
5. 合并R16和L16
6. 逆初始置换(IP⁻¹)
7. 输出64位密文

2. 密钥特性

特性说明
有效长度56位(64位含8位奇偶校验)
密钥调度16轮子密钥生成算法
密钥空间2⁵⁶ ≈ 7.2×10¹⁶种可能
密钥输入64位(含8位校验位)
子密钥16个48位轮密钥

3. 安全设计要素

  • 混淆:通过8个S盒实现非线性变换
  • 扩散:通过P盒置换实现比特扩散
  • 雪崩效应:1比特输入变化导致约50%输出比特改变
  • 抗差分分析:S盒精心设计抵抗差分攻击
  • 轮结构:16轮迭代确保充分混淆扩散

DES加解密详细过程

加密流程

1. 64位明文输入
2. 初始置换IP
3. 分割为L0(左32位)和R0(右32位)
4. 16轮迭代:第1轮:R1 = L0 ⊕ F(R0, K1), L1 = R0第2轮:R2 = L1 ⊕ F(R1, K2), L2 = R1...第16轮:R16 = L15 ⊕ F(R15, K16), L16 = R15
5. 合并R16和L16(注意:不交换最后结果)
6. 逆初始置换IP⁻¹
7. 输出64位密文

轮函数F核心处理

1. 32位输入
2. 扩展置换E:32位→48位
3. 与48位轮密钥异或
4. S盒替换:48位→32位(8个S盒并行处理)
5. P盒置换:32位→32位
6. 32位输出

密钥调度算法

1. 64位密钥输入
2. 置换选择PC-1:64位→56位(移除校验位)
3. 分割为C0(左28位)和D0(右28位)
4. 16轮处理:- 循环左移(1或2位,根据轮次)- 置换选择PC-2:56位→48位(生成轮密钥Ki)
5. 输出16个48位轮密钥(K1-K16)

Java实现(完整注释版)

import java.security.SecureRandom;
import java.util.Arrays;public class DESAlgorithm {private static final int BLOCK_SIZE = 8; // DES分组大小// 初始置换表 (IP)private static final int[] INITIAL_PERMUTATION = {58, 50, 42, 34, 26, 18, 10, 2,60, 52, 44, 36, 28, 20, 12, 4,62, 54, 46, 38, 30, 22, 14, 6,64, 56, 48, 40, 32, 24, 16, 8,57, 49, 41, 33, 25, 17, 9,  1,59, 51, 43, 35, 27, 19, 11, 3,61, 53, 45, 37, 29, 21, 13, 5,63, 55, 47, 39, 31, 23, 15, 7};// 逆初始置换表 (IP⁻¹)private static final int[] INVERSE_PERMUTATION = {40, 8, 48, 16, 56, 24, 64, 32,39, 7, 47, 15, 55, 23, 63, 31,38, 6, 46, 14, 54, 22, 62, 30,37, 5, 45, 13, 53, 21, 61, 29,36, 4, 44, 12, 52, 20, 60, 28,35, 3, 43, 11, 51, 19, 59, 27,34, 2, 42, 10, 50, 18, 58, 26,33, 1, 41, 9, 49, 17, 57, 25};// 扩展置换表 (E)private static final int[] EXPANSION_TABLE = {32,  1,  2,  3,  4,  5,4,  5,  6,  7,  8,  9,8,  9, 10, 11, 12, 13,12, 13, 14, 15, 16, 17,16, 17, 18, 19, 20, 21,20, 21, 22, 23, 24, 25,24, 25, 26, 27, 28, 29,28, 29, 30, 31, 32,  1};// DES标准的8个S盒定义(4行×16列)private static final int[][][] S_BOXES = {// S1{{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},{0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},{4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},// S2{{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},{3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},{0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},// S3{{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},{1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},// S4{{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},{3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},// S5{{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},{4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},// S6{{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},{10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},{9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},{4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},// S7{{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},{13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},{1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},{6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},// S8{{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},{1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},{7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},{2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};// P盒置换表private static final int[] P_BOX_PERMUTATION = {16,  7, 20, 21,29, 12, 28, 17,1, 15, 23, 26,5, 18, 31, 10,2,  8, 24, 14,32, 27,  3,  9,19, 13, 30,  6,22, 11,  4, 25};// 密钥置换表 (PC-1)private static final int[] PC1 = {57, 49, 41, 33, 25, 17, 9,1, 58, 50, 42, 34, 26, 18,10,  2, 59, 51, 43, 35, 27,19, 11,  3, 60, 52, 44, 36,63, 55, 47, 39, 31, 23, 15,7, 62, 54, 46, 38, 30, 22,14,  6, 61, 53, 45, 37, 29,21, 13,  5, 28, 20, 12,  4};// 轮移位表private static final int[] SHIFT_SCHEDULE = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};// 密钥压缩置换 (PC-2)private static final int[] PC2 = {14, 17, 11, 24,  1,  5,3, 28, 15,  6, 21, 10,23, 19, 12,  4, 26,  8,16,  7, 27, 20, 13,  2,41, 52, 31, 37, 47, 55,30, 40, 51, 45, 33, 48,44, 49, 39, 56, 34, 53,46, 42, 50, 36, 29, 32};/*** 执行位置换操作* @param input 输入位数组* @param table 置换表* @return 置换后的位数组*/private static boolean[] permute(boolean[] input, int[] table) {boolean[] output = new boolean[table.length];for (int i = 0; i < table.length; i++) {output[i] = input[table[i] - 1];//注意减去1,因为数组从0开始}return output;}/*** 执行S盒替换 (DES核心非线性变换)* @param input 48位输入* @return 32位输出*/private static boolean[] sBoxSubstitution(boolean[] input) {if (input.length != 48)throw new IllegalArgumentException("S盒输入必须为48位");boolean[] output = new boolean[32];for (int i = 0; i < 8; i++) {// 提取6位输入boolean[] bs = Arrays.copyOfRange(input, i*6, (i+1)*6);// 计算行号 (第1和6位)int row = (bs[0] ? 2 : 0) | (bs[5] ? 1 : 0);//b1和b6位组合// 计算列号 (中间4位)int col = 0;for (int j = 1; j <= 4; j++) {if (bs[j]) col |= (1 << (4 - j));//中间4位组合}// 从S盒获取4位输出int sBoxValue = S_BOXES[i][row][col];// 转换为4位布尔数组int outputPos = 0;for (int j = 0; j < 4; j++) {output[outputPos++] = ((sBoxValue >> (3 - j)) & 1) == 1;}}return output;}/*** 轮函数F* @param right 32位右半部分* @param roundKey 48位轮密钥* @return 32位输出*/private static boolean[] fFunction(boolean[] right, boolean[] roundKey) {// 1. 扩展置换 (32→48位)boolean[] expanded = permute(right, EXPANSION_TABLE);// 2. 与轮密钥异或boolean[] xored = new boolean[48];for (int i = 0; i < 48; i++) {xored[i] = expanded[i] ^ roundKey[i];}// 3. S盒替换 (48→32位)boolean[] substituted = sBoxSubstitution(xored);// 4. P盒置换return permute(substituted, P_BOX_PERMUTATION);}/*** 密钥调度算法 (生成16轮子密钥)* @param key 64位密钥 (含8位奇偶校验)* @return 16个48位轮密钥*/private static boolean[][] generateSubKeys(boolean[] key) {// 1. PC-1置换 (64→56位)boolean[] pc1 = permute(key, PC1);// 2. 分为左右28位boolean[] c0 = Arrays.copyOfRange(pc1, 0, 28);boolean[] d0 = Arrays.copyOfRange(pc1, 28, 56);boolean[][] subKeys = new boolean[16][48];for (int round = 0; round < 16; round++) {// 3. 循环左移int shift = SHIFT_SCHEDULE[round];c0 = rotateLeft(c0, shift);d0 = rotateLeft(d0, shift);// 4. 合并为56位boolean[] cd = new boolean[56];System.arraycopy(c0, 0, cd, 0, 28);System.arraycopy(d0, 0, cd, 28, 28);// 5. PC-2置换 (56→48位)subKeys[round] = permute(cd, PC2);}return subKeys;}/*** 循环左移* @param bits 输入位数组* @param shift 移位位数* @return 移位后位数组*/private static boolean[] rotateLeft(boolean[] bits, int shift) {boolean[] result = new boolean[bits.length];for (int i = 0; i < bits.length; i++) {result[i] = bits[(i + shift) % bits.length];}return result;}/*** DES加密核心* @param block 64位明文分组* @param key 64位密钥* @return 64位密文分组*/public static boolean[] desEncryptBlock(boolean[] block, boolean[] key) {// 1. 生成16轮子密钥boolean[][] subKeys = generateSubKeys(key);// 2. 初始置换IPboolean[] ip = permute(block, INITIAL_PERMUTATION);// 3. 分为左右32位boolean[] left = Arrays.copyOfRange(ip, 0, 32);boolean[] right = Arrays.copyOfRange(ip, 32, 64);// 4. 16轮Feistel迭代for (int round = 0; round < 16; round++) {boolean[] temp = right;boolean[] fResult = fFunction(right, subKeys[round]);// 左半部分与F函数结果异或boolean[] newRight = new boolean[32];for (int i = 0; i < 32; i++) {newRight[i] = left[i] ^ fResult[i];}// 更新左右部分left = temp;right = newRight;}// 5. 最终交换 (R16  L16)boolean[] rl = new boolean[64];System.arraycopy(right, 0, rl, 0, 32);System.arraycopy(left, 0, rl, 32, 32);// 6. 逆初始置换IP⁻¹return permute(rl, INVERSE_PERMUTATION);}/*** DES解密核心* @param block 64位密文分组* @param key 64位密钥* @return 64位明文分组*/public static boolean[] desDecryptBlock(boolean[] block, boolean[] key) {// 生成子密钥boolean[][] subKeys = generateSubKeys(key);// 解密时逆序使用子密钥boolean[][] reverseKeys = new boolean[16][48];for (int i = 0; i < 16; i++) {reverseKeys[i] = subKeys[15 - i];}// 其余步骤与加密相同boolean[] ip = permute(block, INITIAL_PERMUTATION);boolean[] left = Arrays.copyOfRange(ip, 0, 32);boolean[] right = Arrays.copyOfRange(ip, 32, 64);for (int round = 0; round < 16; round++) {boolean[] temp = right;boolean[] fResult = fFunction(right, reverseKeys[round]);boolean[] newRight = new boolean[32];for (int i = 0; i < 32; i++) {newRight[i] = left[i] ^ fResult[i];}left = temp;right = newRight;}boolean[] rl = new boolean[64];System.arraycopy(right, 0, rl, 0, 32);System.arraycopy(left, 0, rl, 32, 32);return permute(rl, INVERSE_PERMUTATION);}/*** 字节数组转位数组* @param bytes 字节数组* @return 位数组*/public static boolean[] bytesToBits(byte[] bytes) {boolean[] bits = new boolean[bytes.length * 8];for (int i = 0; i < bytes.length; i++) {for (int j = 0; j < 8; j++) {bits[i * 8 + j] = ((bytes[i] >> (7 - j)) & 1) == 1;}}return bits;}/*** 位数组转字节数组* @param bits 位数组* @return 字节数组*/public static byte[] bitsToBytes(boolean[] bits) {byte[] bytes = new byte[bits.length / 8];for (int i = 0; i < bytes.length; i++) {for (int j = 0; j < 8; j++) {if (bits[i * 8 + j]) {bytes[i] |= (1 << (7 - j));}}}return bytes;}public static byte[] generateRandomKey() {SecureRandom random = new SecureRandom();byte[] key = new byte[8]; // 64位密钥random.nextBytes(key);// 设置奇偶校验位(DES标准要求)for (int i = 0; i < 8; i++) {int parity = Integer.bitCount(key[i] & 0xFF) % 2;key[i] = (byte) ((key[i] & 0xFE) | (parity == 0 ? 1 : 0));}return key;}/*** PKCS#7填充* @param data 需要填充的数据* @param blockSize 块大小(DES为8字节)* @return 填充后的数据*/public static byte[] pkcs7Pad(byte[] data, int blockSize) {int paddingLength = blockSize - (data.length % blockSize);byte[] padded = new byte[data.length + paddingLength];System.arraycopy(data, 0, padded, 0, data.length);// 填充字节的值等于填充长度Arrays.fill(padded, data.length, padded.length, (byte) paddingLength);return padded;}/*** PKCS#7去除填充* @param data 带填充的数据* @return 去除填充后的数据* @throws IllegalArgumentException 如果填充无效*/public static byte[] pkcs7Unpad(byte[] data) {// 检查填充长度是否有效int paddingLength = data[data.length - 1] & 0xFF;if (paddingLength < 1 || paddingLength > data.length) {throw new IllegalArgumentException("无效的PKCS#7填充");}// 验证所有填充字节是否正确for (int i = data.length - paddingLength; i < data.length; i++) {if (data[i] != paddingLength) {throw new IllegalArgumentException("无效的PKCS#7填充");}}return Arrays.copyOfRange(data, 0, data.length - paddingLength);}/*** DES加密长文本* @param plaintext 明文* @param key 64位密钥(8字节)* @return 密文*/public static byte[] encrypt(byte[] plaintext, byte[] key) {// 1. 应用PKCS#7填充byte[] padded = pkcs7Pad(plaintext, 8);// 2. 将密钥转换为位数组boolean[] keyBits = bytesToBits(key);// 3. 分组加密byte[] ciphertext = new byte[padded.length];for (int i = 0; i < padded.length; i += 8) {// 提取当前分组byte[] block = Arrays.copyOfRange(padded, i, i + 8);boolean[] blockBits = bytesToBits(block);// 加密分组boolean[] encryptedBits = desEncryptBlock(blockBits, keyBits);// 转换回字节并存储byte[] encryptedBlock = bitsToBytes(encryptedBits);System.arraycopy(encryptedBlock, 0, ciphertext, i, 8);}return ciphertext;}/*** DES解密长文本* @param ciphertext 密文* @param key 64位密钥(8字节)* @return 明文*/public static byte[] decrypt(byte[] ciphertext, byte[] key) {// 1. 检查输入长度if (ciphertext.length % 8 != 0) {throw new IllegalArgumentException("密文长度必须是8的倍数");}// 2. 将密钥转换为位数组boolean[] keyBits = bytesToBits(key);// 3. 分组解密byte[] decryptedWithPadding = new byte[ciphertext.length];for (int i = 0; i < ciphertext.length; i += 8) {// 提取当前分组byte[] block = Arrays.copyOfRange(ciphertext, i, i + 8);boolean[] blockBits = bytesToBits(block);// 解密分组boolean[] decryptedBits = desDecryptBlock(blockBits, keyBits);// 转换回字节并存储byte[] decryptedBlock = bitsToBytes(decryptedBits);System.arraycopy(decryptedBlock, 0, decryptedWithPadding, i, 8);}// 4. 去除填充return pkcs7Unpad(decryptedWithPadding);}}
  • 演示
public class Main {public static void main(String[] args) {byte[] key=DESAlgorithm.generateRandomKey();//生成随机密钥String str="尽管DES已完成历史使命,其精巧的Feistel结构设计、S盒构造方法和密钥调度算法,仍为密码学研究者提供了宝贵的学习范例。理解DES的工作原理,是掌握现代密码学不可或缺的基础。";byte[] msg= str.getBytes();byte[] c = DESAlgorithm.encrypt(msg, key);//加密String encryptedText = new String(c);byte[] m = DESAlgorithm.decrypt(c,key);//解密String decryptedText = new String(m);System.out.println(encryptedText);System.out.println(decryptedText);}
}

在这里插入图片描述

DES安全分析与演进

安全漏洞

  1. 密钥长度不足:56位密钥易受暴力破解
  2. 弱密钥问题:某些密钥导致加密强度降低
  3. 半弱密钥:某些密钥对产生可逆加密
  4. 线性与差分分析:理论攻击方法复杂度为2⁴³

增强方案:3DES

加密:C = E(K3, D(K2, E(K1, P)))
解密:P = D(K1, E(K2, D(K3, C)))

3DES Java实现

public static byte[] tripleDESEncrypt(byte[] plaintext, byte[] key) throws Exception {if (key.length != 24) throw new IllegalArgumentException("3DES需要24字节密钥");byte[] key1 = Arrays.copyOfRange(key, 0, 8);byte[] key2 = Arrays.copyOfRange(key, 8, 16);byte[] key3 = Arrays.copyOfRange(key, 16, 24);// 加密:E(K3, D(K2, E(K1, P)))byte[] step1 = encryptWithJCE(plaintext, key1);byte[] step2 = decryptWithJCE(step1, key2);return encryptWithJCE(step2, key3);
}

DES与AES对比

特性DESAES
密钥长度56位128/192/256位
分组大小64位128位
结构Feistel网络SP网络
轮数1610/12/14
安全性已破解当前安全
性能较慢更快
应用遗留系统现代标准

实际应用建议

  1. 避免使用纯DES:使用3DES或AES替代

  2. 选择安全模式

    // 推荐使用CBC或CTR模式
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    
  3. 密钥管理最佳实践

    // 使用安全随机数生成密钥
    KeyGenerator keyGen = KeyGenerator.getInstance("AES");
    keyGen.init(256); // 256位密钥
    SecretKey secretKey = keyGen.generateKey();
    
  4. 完整性保护

    // 结合HMAC或使用认证加密模式
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    

历史意义与总结

DES作为密码学发展史上的里程碑:

  1. 开创了公开密码算法的标准化时代
  2. 推动了分组密码设计理论研究
  3. 促进了密码分析技术的进步
  4. 为现代加密标准(如AES)奠定了基础

尽管DES已完成历史使命,其精巧的Feistel结构设计、S盒构造方法和密钥调度算法,仍为密码学研究者提供了宝贵的学习范例。理解DES的工作原理,是掌握现代密码学不可或缺的基础。


http://www.hkcw.cn/article/SZaILRPapZ.shtml

相关文章

web程序设计期末复习-简答题

页面色彩搭配的基本原理 色彩的鲜明性。网页的色彩要鲜艳&#xff0c;容易引人注目。色彩的独特性。要有与众不同的色彩&#xff0c;使得大家对你的印象强烈。色彩的合适性。就是说色彩和你表达的内容气氛相合适。如用粉色体现女性站点的柔性。色彩的联想性。不同色彩会产生不…

pikachu靶场通关笔记11 XSS关卡07-XSS之关键字过滤绕过(三种方法渗透)

目录 一、源码分析 1、进入靶场 2、代码审计 3、攻击思路 二、渗透实战 1、探测过滤信息 2、注入Payload1 3、注入Payload2 4、注入Payload3 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#xff0c;通过对XSS关卡源码的代码审计找到安…

基于Java的OPCDA采集中间件

1.软件功能及技术特点简介&#xff1a; 软件功能及技术特点简介&#xff1a; OPCDA是基于Java语言开发的OPC client&#xff08;OPC客户端&#xff09;跨平台中间件软件&#xff0c;他支持OPC SERVER的OPC DA1.0/2.0/3.0。OPCDA实时采集数据&#xff08;包括实时数据、报警数…

centos挂载目录满但实际未满引发系统宕机

测试服务器应用系统突然挂了&#xff0c;经过排查发现是因为磁盘“满了”导致的&#xff0c;使用df -h查看磁盘使用情况/home目录使用率已经到了100%,但使用du -sh /home查看发现实际磁盘使用还不到1G&#xff0c;推测有进程正在写入或占用已删除的大文件&#xff08;Linux 系统…

鸿蒙仓颉语言开发实战教程:购物车页面

大家上午好&#xff0c;仓颉语言商城应用的开发进程已经过半&#xff0c;不知道大家通过这一系列的教程对仓颉开发是否有了进一步的了解。今天要分享的购物车页面&#xff1a; 看到这个页面&#xff0c;我们首先要对它简单的分析一下。这个页面一共分为三部分&#xff0c;分别是…

Redisson单机模式

redisson调用unlock的过程 Redisson 是一个基于 Redis 的 Java 驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;框架&#xff0c;提供了分布式和可扩展的数据结构和服务。Redisson 的 unlock 方法用于释放锁。下面是 unlock 方法的调用过程&#xff1a; 获取锁的状…

软件测试环境搭建与测试流程

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1.软件测试环境搭建 思考&#xff1a; 在什么条件下做软件测试&#xff1f;怎么做软件测试&#xff1f; 1.1 搭建测试环境前 确定测试目的 功能测试&#xff…

Go语言学习-->从零开始搭建环境

Go语言学习–>从零开始搭建环境 1 开发环境 Go官网下载地址&#xff1a;https://golang.org/dl/ Go官方镜像站&#xff08;推荐&#xff09;&#xff1a;https://golang.google.cn/dl/ windos 平台下载&#xff1a; 我这里下载1.22稳定版 双击下载好的.msi文件 修改安装…

Mac 芯片系列 安装cocoapod 教程

安装声明&#xff1a; 本人是在搭梯子的环境下安装成功&#xff0c;前提是必须安装好安装homebrew环境。 1.检测rudy的源 2.查看源(目的:检测rudy的源) gem sources - l 3.移除源(目的:移除rudy自带的源) gem sources --remove https://rubygems.org/ 4.更换源(目的:替换成国…

idea不识别lombok---实体类报没有getter方法

介绍 本篇文章&#xff0c;主要讲idea引入lombok后&#xff0c;在实体类中加注解Data&#xff0c;在项目启动的时候&#xff0c;编译不通过&#xff0c;报错xxx.java没有getXxxx&#xff08;&#xff09;方法。 原因有以下几种 1. idea没有开启lombok插件 2. 使用idea-2023…

JavaWeb是什么?总结一下JavaWeb的体系

一&#xff1a;Maven 1.1 定义 不需要导入依赖&#xff0c;在配置文件描述配置信息&#xff0c;maven会自动导入 统一项目结构&#xff1a; 项目构建&#xff1a; 1.2 ideal集成 &#xff08;1&#xff09;maven配置 &#xff08;2&#xff09;创建maven项目 ‘ &#xff08;3&…

MEMCPY引发的非对齐访问

目录 前言背景代码直接运行的现象问题原因解决办法 前言 1&#xff0c;memcpy在keil中是伪装成函数的C语言关键字&#xff0c;有可能会被优化为字对齐形式__rt_memcpy_w 2&#xff0c;编译到memcpy位置的时候&#xff0c;编译器会检查地址类型&#xff0c;如果两个指针都是4字…

CSS(2)

文章目录 Emmet语法快速生成HTML结构语法 Snipaste快速生成CSS样式语法快速格式化代码 快捷键&#xff08;VScode&#xff09;CSS 的复合选择器什么是复合选择器交集选择器后代选择器(重要)子选择器(重要&#xff09;并集选择器(重要&#xff09;**链接伪类选择器**focus伪类选…

AI+在线教育系统源码:开发智能化互动网校平台全流程详解

在数字化浪潮席卷各行各业的当下&#xff0c;教育行业也不例外。从最早的PPT网课&#xff0c;到今天“AI互动教学”深度融合的智能网校系统&#xff0c;在线教育平台的底层逻辑和技术架构已然发生翻天覆地的变化。今天&#xff0c;笔者将为正计划进入在线教育领域的创业者、产品…

Prj09--8088单板机C语言8253产生1KHz方波(1)

1.8253原理图 2.Deepseek给出的参考程序 #include <stdio.h> #include <conio.h> #include <dos.h>// 8253定时器端口定义 #define PORT_8253_CNT0 0x9000 // 计数器0地址 #define PORT_8253_CNT1 0x9001 // 计数器1地址 #define PORT_8253_CNT2 …

女儿回应48岁妈妈顺产得子 意外怀孕引发热议

女儿回应48岁妈妈顺产得子。近日,广东河源一位48岁的再婚妈妈怀孕7个月自己都没察觉,还以为是“绝经发福”了。她28岁的女儿透露,妈妈和现任丈夫相识仅40天就闪婚,已经共同生活了7年。此前医生曾诊断这位妈妈没有卵泡无法怀孕,没想到却意外怀上了。女儿最初心情复杂,但现…

js-day7

JS学习之旅-day7 1.事件流1.1 事件流与两个阶段说明1.2 事件捕获1.3 事件冒泡1.4 阻止1.5 解绑事件 2. 事件委托3. 其他事件3.1 页面加载事件3.2 页面滚动事件3.3 页面尺寸事件 4. 元素尺寸与位置 1.事件流 1.1 事件流与两个阶段说明 事件流指的是事件完整执行过程中的流动路…

【Typst】5.文档结构元素与函数

概述 本节介绍Typst文档的核心文档结构元素及其对应函数&#xff0c;还有函数的用法。通过本节你将可以更好的使用脚本创建和控制页面元素。 系列目录 1.Typst概述2.Typst标记语法和基础样式3.Typst脚本语法4.导入、包含和读取5.文档结构元素与函数6.布局函数 set语句和sho…

每日八股文6.3

每日八股-6.3 Mysql1.COUNT 作用于主键列和非主键列时&#xff0c;结果会有不同吗&#xff1f;2.MySQL 中的内连接&#xff08;INNER JOIN&#xff09;和外连接&#xff08;OUTER JOIN&#xff09;有什么主要的区别&#xff1f;3.能详细描述一下 MySQL 执行一条查询 SQL 语句的…

【Linux】pthread多线程同步

参考文章&#xff1a;https://blog.csdn.net/Alkaid2000/article/details/128121066 一、线程同步 线程的主要优势在于&#xff0c;能够通过全局变量来共享信息。不过&#xff0c;这种便携的共享是有代价的&#xff1b;必须确保多个线程不会同时修改同一变量&#xff0c;或者某…