## 第 1 题:综合应用——用户注册验证系统(异常处理综合)⭐⭐⭐⭐ ### 需求说明 开发一个用户注册验证系统,对用户输入的注册信息进行校验。当输入不合法时,使用 **自定义异常** 和 **多 catch 处理** 给出明确提示。 ### 自定义异常类 定义以下自定义异常类(继承 `Exception` —— 编译期异常): ```java // 用户名不能为空异常 class UsernameEmptyException extends Exception { public UsernameEmptyException(String message) { super(message); } } // 用户名长度异常(长度必须在 3~12 个字符之间) class UsernameLengthException extends Exception { public UsernameLengthException(String message) { super(message); } } // 密码不能为空异常 class PasswordEmptyException extends Exception { public PasswordEmptyException(String message) { super(message); } } // 密码强度异常(必须包含数字和字母,长度 >= 6) class PasswordWeakException extends Exception { public PasswordWeakException(String message) { super(message); } } // 年龄异常(年龄必须在 0~150 之间) class AgeInvalidException extends Exception { public AgeInvalidException(String message) { super(message); } } ``` ### RegisterService 类 ```java public class RegisterService { /** * 验证用户名 * @param username 用户名 * @throws UsernameEmptyException 用户名为空 * @throws UsernameLengthException 用户名长度不在 3~12 之间 */ public static void validateUsername(String username) throws UsernameEmptyException, UsernameLengthException { // 1. 如果 username 为 null 或 trim() 后长度为 0,抛出 UsernameEmptyException // 提示信息:"用户名不能为空!" // 2. 如果 username.length() < 3 或 > 12,抛出 UsernameLengthException // 提示信息:"用户名长度必须在 3~12 个字符之间!" // 请补充代码 } /** * 验证密码 * @param password 密码 * @throws PasswordEmptyException 密码为空 * @throws PasswordWeakException 密码强度不足 */ public static void validatePassword(String password) throws PasswordEmptyException, PasswordWeakException { // 1. 如果 password 为 null 或 trim() 后长度为 0,抛出 PasswordEmptyException // 提示信息:"密码不能为空!" // 2. 如果 password.length() < 6,抛出 PasswordWeakException // 提示信息:"密码长度不能少于 6 位!" // 3. 如果 password 不包含数字或字母(判断技巧:使用正则或循环遍历逐个字符检查), // 抛出 PasswordWeakException // 提示信息:"密码必须包含数字和字母!" // 请补充代码(建议:使用循环遍历 + 两个 boolean 标记) } /** * 验证年龄 * @param age 年龄字符串 * @throws NumberFormatException 年龄不是数字 * @throws AgeInvalidException 年龄超出范围 */ public static void validateAge(String age) throws NumberFormatException, AgeInvalidException { // 1. 使用 Integer.parseInt(age) 转换为 int // 如果 age 不是有效数字,NumberFormatException 会自动抛出(无需手动处理) // 2. 如果 age < 0 或 age > 150,抛出 AgeInvalidException // 提示信息:"年龄必须在 0~150 之间!" // 请补充代码 } /** * 注册用户 * @param username 用户名 * @param password 密码 * @param age 年龄 */ public static void register(String username, String password, String age) { // 要求: // 1. 使用 try-catch-finally 调用各验证方法 // 2. 按顺序验证:用户名 → 密码 → 年龄 // 3. 每个验证都有独立的 catch 块,输出对应的错误提示 // 4. 如果所有验证都通过,输出:"注册成功!欢迎 " + username // 5. 注意:validateAge() 可能抛出 NumberFormatException,也需要捕获 // 6. 【新增】添加 finally 块,无论注册成功还是失败,都输出: // "—— 注册操作日志:用户名=" + username + ",结果=" + (是否成功 ? "成功" : "失败") // 提示:可以在 try 块成功时用一个 boolean 变量标记成功,finally 中读取该变量 // 请补充代码 } public static void main(String[] args) { // 测试用例 System.out.println("========== 测试 1:正常注册 =========="); register("张三丰", "abc123", "25"); System.out.println("\n========== 测试 2:用户名为空 =========="); register("", "abc123", "25"); System.out.println("\n========== 测试 3:用户名太短 =========="); register("12", "abc123", "25"); System.out.println("\n========== 测试 4:密码太短 =========="); register("张三丰", "12", "25"); System.out.println("\n========== 测试 5:密码不包含数字 =========="); register("张三丰", "abcdef", "25"); System.out.println("\n========== 测试 6:年龄不是数字 =========="); register("张三丰", "abc123", "二十五"); System.out.println("\n========== 测试 7:年龄超出范围 =========="); register("张三丰", "abc123", "200"); System.out.println("\n========== 测试 8:综合错误(多错误按顺序只报第一个)=========="); register("", "", "abc"); // 应该只报用户名错误,不会检查密码和年龄 } } ``` ### 预期输出(含 finally 操作日志) ``` ========== 测试 1:正常注册 ========== 注册成功!欢迎 张三丰 —— 注册操作日志:用户名=张三丰,结果=成功 ========== 测试 2:用户名为空 ========== 用户名不能为空! —— 注册操作日志:用户名=,结果=失败 ========== 测试 3:用户名太短 ========== 用户名长度必须在 3~12 个字符之间! —— 注册操作日志:用户名=12,结果=失败 ========== 测试 4:密码太短 ========== 密码长度不能少于 6 位! —— 注册操作日志:用户名=张三丰,结果=失败 ========== 测试 5:密码不包含数字 ========== 密码必须包含数字和字母! —— 注册操作日志:用户名=张三丰,结果=失败 ========== 测试 6:年龄不是数字 ========== 年龄格式错误:二十五 不是有效的数字! —— 注册操作日志:用户名=张三丰,结果=失败 ========== 测试 7:年龄超出范围 ========== 年龄必须在 0~150 之间! —— 注册操作日志:用户名=张三丰,结果=失败 ========== 测试 8:综合错误 ========== 用户名不能为空! —— 注册操作日志:用户名=,结果=失败 ``` ### 思考题 1. 本练习中,自定义异常都继承了 `Exception`(编译期异常),这有什么影响?如果改为继承 `RuntimeException`,代码会有什么变化? 2. 为什么 `register()` 方法中,每个验证方法都有自己的 catch 块,而不是用一个大 `catch (Exception e)` 统一处理?这样做的好处是什么? 3. 测试 8 中,用户名、密码、年龄同时出错,为什么只报了用户名的错误?这体现了什么设计思想? 4. 自定义异常在实际项目中有哪些应用场景?什么时候应该自定义异常而不是使用 Java 内置异常? 5. **【finally 思考】** 本练习中 `register()` 方法使用 `finally` 输出了操作日志。如果不用 `finally`,把日志输出放在 try 块末尾和每个 catch 块中分别写一次,是否能达到同样效果?这样做有什么弊端?(提示:代码重复、遗漏风险) 6. **【finally 思考】** 如果 try 块中有 `return` 语句,`finally` 块还会执行吗?请编写一个小测试验证你的结论。