题目.md 8.0 KB

第 1 题:综合应用——用户注册验证系统(异常处理综合)⭐⭐⭐⭐

需求说明

开发一个用户注册验证系统,对用户输入的注册信息进行校验。当输入不合法时,使用 自定义异常 和 多 catch 处理 给出明确提示。

自定义异常类

定义以下自定义异常类(继承 Exception —— 编译期异常):

// 用户名不能为空异常
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 类

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 块还会执行吗?请编写一个小测试验证你的结论。