如何使用Java实现用户输入校验_Java异常处理实战讲解

Java输入校验需用hasNextInt()预判+nextLine()清缓存;自定义RuntimeException细化业务异常;用Optional替代null避免NPE;Spring中@Valid须配BindingResult捕获错误。

Java中用Scanner校验用户输入是否为有效整数

用户输入非数字时,Scanner.nextInt() 会抛出 InputMismatchException,而不是返回默认值或静默失败。直接调用它不做处理,程序会中断——这不是校验,是崩溃。

正确做法是配合 hasNextInt() 预判 + nextLine() 清缓冲区:

Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数:");
while (!scanner.hasNextInt()) {
    System.out.print("输入无效,请输入整数:");
    scanner.nextLine(); // 必须清掉非法输入,否则死循环
}
int value = scanner.nextInt();
scanner.close();
  • hasNextInt() 只检查,不消费输入;nextInt() 才真正读取并移除该token
  • 漏掉 scanner.nextLine() 会导致后续输入被残留字符干扰(比如输了个"a"后,下一次 hasNextInt() 仍看到"a")
  • 不要在循环里反复 new Scanner(System.in),标准输入流只需一个实例

自定义异常类封装业务校验逻辑

内置异常如 IllegalArgumentException 虽可用,但无法体现“用户名长度不足”“邮箱格式错误”等具体语义。抛出泛型异常会让上层难以区分处理逻辑。

建议为每类校验定义独立异常,例如:

public class UsernameTooShortException extends RuntimeException {
    public UsernameTooShortException(String username) {
        super("用户名 '" + username + "' 长度不能少于3位");
    }
}
  • 继承 RuntimeException 可避免强制 try-catch,适合参数校验这类编程期错误
  • 构造函数中拼接上下文信息(如原始输入值),比只抛 "用户名太短" 更利于排查
  • 不要把校验逻辑和异常定义混在一个方法里;校验失败时明确 throw new UsernameTooShortException(input)

用Optional替代null返回做输入合法性表达

很多初学者习惯让校验方法返回 null 表示失败,比如 parseEmail(String s) 成功返回 Email 对象、失败返回 null。这迫使调用方写一堆 if (email != null),且容易引发 NullPointerException

改用 Optional 更安全、语义更清晰:

public Optional parseEmail(String input) {
    if (input == null || !input.matches("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
        return Optional.empty();
    }
    return Optional.of(new Email(input));
}
  • 调用方必须显式处理空值:parseEmail(s).ifPresentOrElse(this::sendMail, () -> log.warn("邮箱无效"))
  • 禁止对 Optional 做 .get() 直接取值——那和写 if (x != null) x.method() 没本质区别
  • 若校验失败需附带错误原因,用 Optional.empty() 不够,此时应抛异常或返回 Result(需自行定义)

Spring Boot中@Valid与BindingResult配合处理HTTP表单校验

Web场景下,前端传来的JSON或表单数据,不应在Controller里手写一堆 if (s == null || s.length() 。Spring的声明式校验能自动拦截并返回400响应。

关键点在于:必须紧邻 @Valid 参数后声明 BindingResult,否则校验失败直接抛 MethodArgumentNotValidException(返回500而非400):

@PostMapping("/users")
public ResponseEntity createUser(@Valid @RequestBody User user, BindingResult result) {
    if (result.hasErrors()) {
        List errors = result.getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .collect(Collectors.toList());
        return ResponseEntity.badRequest().body(errors);
    }
    // 正常处理...
}
  • @Valid 本身不会阻止方法执行;只有配对 BindingResult 才能捕获错误
  • 实体类字段需加注解,如 @NotBlank(message = "用户名不能为空")@Min(value = 18, message = "年龄不能小于18")
  • 若用 @Validated 分组校验,BindingRe

    sult
    仍需紧跟其后,顺序不能错
实际项目中最容易被忽略的是输入缓冲区清理和 BindingResult 的位置约束——前者导致控制台卡死,后者让本该400的请求变成500,调试时往往绕半天才意识到是语法顺序问题。