验证码字符与算术题,这个Java工具类6个方法只产字符串
## 📋 目录 - [一、工具类功能](#sec-func) - [二、所需环境](#sec-env) - [三、方法与调用示例(按需调用)](#sec-method) ## 一、工具类功能 生成短信码、邮箱码与图形码上要印的那串字符,另出「3 + 5 = ?」这类算术题,四类字符集各有剔除口径,随机源是共享的 `SecureRandom`,本类只产字符串、不负责画图。 ## 二、所需环境 JDK 17或以上 ## 三、方法与调用示例(按需调用) 🔢 **方法序号**:1 🎯 **方法用途**: - 生成纯数字验证码,短信码、邮箱一次性码这类只要数字的场景直接用它 - 要印在图形码上的那串文本也可以先拿它出码,再交给画图的那一步 🧩 **方法定义**:`public static String digits(int length)` 🗣️ **入参解释**:`public static String digits(字符长度)` 📤 **返回值解释**:`String`,指定长度的数字串 💡 **边界口径**: - 字符集是完整的 0 到 9 共 10 个数字,首位同样可为 0 - 不剔除易混字符,要避开 0 与 1 走 `mixed` - 长度必须落在 1 到 128,越界抛 `IllegalArgumentException` 并带上实际值 - 随机源是类内共享的 `SecureRandom`,多线程直接并发调用,别换成 `Random` 或 `Math.random()` 🧪 **方法示例**: - `digits(6)` 拿到 `013803`,首位就是 0 - `digits(6)` 再拿一次是 `864326`,同一个长度每次给出的码不同 - 循环 500 轮取 `digits(6)` 的首位判 0,拿到 `true`,首位为 0 的组合确实会出现 - 连跑 500 次 `digits(6).matches("[0-9]{6}")` 拿到 `true` - `digits(0)` 抛 `长度必须大于0,当前为0` - `digits(129)` 抛 `长度不能超过128,当前为129` --- 🔢 **方法序号**:2 🎯 **方法用途**: - 生成大写字母验证码,字符集去掉易混的 I 与 O,保留 L - 券码、邀请码这种要人肉眼辨认的场合用纯字母比混数字好认 🧩 **方法定义**:`public static String upperLetters(int length)` 🗣️ **入参解释**:`public static String upperLetters(字符长度)` 📤 **返回值解释**:`String`,指定长度的大写字母串 💡 **边界口径**: - 字符集是去掉 I、O 的 24 个大写字母 - 长度的校验口径与 `digits` 一致,上限 128 - 串里不会出现数字,也不区分小写形态 🧪 **方法示例**: - `upperLetters(4)` 拿到 `SMSG`,样本值每次跑都不一样 - 连跑 500 次 `upperLetters(8).matches("[A-HJ-NP-Z]{8}")` 拿到 `true`,I 与 O 一次都没出现 --- 🔢 **方法序号**:3 🎯 **方法用途**: - 生成小写字母验证码,字符集去掉 i、l、o 这三个易混字符 - 页面上一串小写码、和大小写无关的口令提示用这一版 🧩 **方法定义**:`public static String lowerLetters(int length)` 🗣️ **入参解释**:`public static String lowerLetters(字符长度)` 📤 **返回值解释**:`String`,指定长度的小写字母串 💡 **边界口径**: - 字符集是去掉 i、l、o 的 23 个小写字母,j、k、m、n、p 都保留 - 长度校验与上限同 `digits` - 与大写版互不相干,要大小写都有走 `mixed` 🧪 **方法示例**: - `lowerLetters(6)` 拿到 `nxccjr`,样本值每次跑都不一样 - 连跑 500 次 `lowerLetters(8).matches("[a-hj-kmnp-z]{8}")` 拿到 `true` --- 🔢 **方法序号**:4 🎯 **方法用途**: - 生成数字加大小写字母的混合码,同长度下空间比单一字符集大得多 - 图形验证码上要印的那串文本、需要短而强的凭证时用这一版 🧩 **方法定义**:`public static String mixed(int length)` 🗣️ **入参解释**:`public static String mixed(字符长度)` 📤 **返回值解释**:`String`,指定长度的混合字符串 💡 **边界口径**: - 字符集共 54 个:数字去掉 0 与 1,大写去掉 I、L、O,小写去掉 i、l、o - 结果区分大小写,校验时要按大小写敏感处理,或者自己先统一大小写 - 长度上限同样是 128,越界抛 `IllegalArgumentException` 🧪 **方法示例**: - `mixed(4)` 拿到 `yNzU`,样本值每次跑都不一样 - `mixed(128).length()` 拿到 `128`,单次生成上限就是 128 个字符 - 连跑 500 次 `mixed(10).matches("[2-9A-HJ-NP-Za-hj-kmnp-z]{10}")` 拿到 `true` --- 🔢 **方法序号**:5 🎯 **方法用途**: - 出一道「3 + 5 = ?」式的算术题,题目和正确答案成对返回 - 挡机器人比字符码轻松,加减乘三种题型等概率出 🧩 **方法定义**:`public static ArithmeticCode arithmetic(int maxOperand)` 🗣️ **入参解释**:`public static ArithmeticCode arithmetic(操作数上限)` 📤 **返回值解释**:`ArithmeticCode`,`question` 是题目串,`answer` 是正确答案 💡 **边界口径**: - 减法先交换两个操作数,保证答案不为负 - 操作数默认取自 `0` 到 `maxOperand`,加法按 `(Integer.MAX_VALUE - 1) / 2` 收窄、乘法按 46340 收窄,答案不会溢出 `int` - 题目里的乘号是 `×` 不是 `*`,前端直接展示即可,校验答案别拿题目串去算 - `maxOperand` 小于等于 0 抛 `IllegalArgumentException`,消息带上实际值 🧪 **方法示例**: - `arithmetic(20)` 拿到 `[3 + 17 = ?] 答案 20`,用题目反算与答案一致 - `arithmetic(20)` 也能出 `[20 - 10 = ?] 答案 10`,被减数总在前面 - `arithmetic(20)` 还能出 `[10 × 4 = ?] 答案 40`,乘号是 `×` - 连跑 2000 轮判断 `answer() >= 0` 拿到 `true` - `arithmetic(2_000_000_000)` 拿到 `[25874 × 7766 = ?] 答案 200937484`,乘法操作数被收窄到 46340 以内 - `arithmetic(Integer.MAX_VALUE)` 拿到 `[231234784 - 133571853 = ?] 答案 97662931`,减法不收窄也能算对 - `arithmetic(0)` 抛 `操作数最大值必须大于0,当前为0` - `arithmetic(-5)` 抛 `操作数最大值必须大于0,当前为-5` --- 🔢 **方法序号**:6 🎯 **方法用途**: - 自己装配一道题和它的答案,比如后端已经算好的自定义题型 - 与 `arithmetic` 返回的是同一个记录类型,下游校验逻辑不用分叉 🧩 **方法定义**:`public record ArithmeticCode(String question, int answer)` 🗣️ **入参解释**:`public record ArithmeticCode(题目, 正确答案)` 📤 **返回值解释**:`ArithmeticCode`,题目与答案成对保存 💡 **边界口径**: - 题目为 null 抛 `NullPointerException`,答案不做任何范围校验 - 记录自带 `equals`,同题同答案判等为真,比对时用答案字段而不是拿题目串算 - `question` 原样保存,工具类不会再拼一遍 🧪 **方法示例**: - `new ArithmeticCode("3 + 5 = ?", 8)` 拿到 `question=[3 + 5 = ?],answer=8` - 与字段相同的另一个对象做等值判断,拿到 `true` - `new ArithmeticCode(null, 1)` 抛 `题目不能为null`