15.2 用户登录
给 CRM 装一扇大门:程序启动先验身份(用户名 + 密码),对了才放行进主菜单,最多给三次机会。技术上全是老朋友——equals 比较(6.2 的老账正式上岗)、循环控制次数(2.5)、方法封装返回 boolean(2.6)。看着简单,字符串比较和次数边界两处坑,新手常年翻车。
一、登录是 CRM 的门卫
启动链定型:Login 验明正身 → CustomerView 主菜单 → 增删改查。门卫逻辑独立成一个环节(单独的类或 login() 方法),和业务功能互不掺和——15.1 分层思想的第一次应用。
门卫的活就三件:收证(读输入)、验证(equals 比较)、放行或拦下(三次机会控制)。
二、核心武器:equals 双校验
"admin".equals(name) && "123456".equals(pwd)
两个讲究,全是 6.2 的老账:
- 必须 equals,不能 ==:
==比的是地址。键盘输入的"admin"和代码里的字面量"admin"不是同一个对象,用==永远 false——输对了也报错,最迷惑的新手 Bug。 - 常量打头,变量在后:
"admin".equals(name)而不是name.equals("admin")——万一 name 是 null,前者老老实实返回 false,后者直接空指针(NPE)。口诀:常量打头,防 null 空针。
三、三次机会:两种骨架随便挑
骨架一:for 倒序(剩余次数白送)
import java.util.Scanner;
public class Demo32 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
for (int i = 2; i >= 0; i--) { // i = 2,1,0,共三次
System.out.print("请输入用户名:");
String name = sc.next();
System.out.print("请输入密码:");
String pwd = sc.next();
if ("admin".equals(name) && "123456".equals(pwd)) {
System.out.println("登录成功,欢迎回来!");
// TODO:进入 CRM 主菜单(CustomerView 的入口)
return; // 门卫放行,方法收工
}
if (i > 0) {
System.out.println("用户名或密码错误!还剩 " + i + " 次机会");
} else {
System.out.println("三次均失败,系统退出。拜拜!");
}
}
}
}
骨架二:while + 计数器(更直白)
int count = 3; // 剩余次数
while (count > 0) {
// ...读输入、校验...
if (对了) { return; } // 或 break 出去
count--; // 错了扣一次
System.out.println("还剩 " + count + " 次机会");
}
System.out.println("三次均失败,系统退出。");
两种都对,选顺眼的。边界是命门:i = 2; i >= 0 到底跑几圈?count-- 放在提示前还是后?——报错一次就该在纸上走一遍流程,别靠感觉。
四、进阶姿势:封装成 boolean 方法
把门卫逻辑收进方法,main 变干净:
public class Login {
public static boolean login() {
Scanner sc = new Scanner(System.in);
for (int i = 2; i >= 0; i--) {
System.out.print("用户名:");
String name = sc.next();
System.out.print("密码:");
String pwd = sc.next();
if ("admin".equals(name) && "123456".equals(pwd)) {
return true; // 放行
}
System.out.println(i > 0 ? "错了!还剩 " + i + " 次" : "机会用完!");
}
return false; // 三次全错
}
public static void main(String[] args) {
if (login()) {
System.out.println("=== 欢迎进入客户关系管理系统 ===");
// TODO:这里启动 CustomerView 主菜单
} else {
System.out.println("系统退出。");
}
}
}
好处:main 只看结果,不看过程——门卫怎么验的三次,主菜单不关心。这就是"一个方法干一件事"(2.6)在项目里的样子。
把校验改成 name == "admin"——输对了也报错。键盘输入的字符串和字面量不是同一个对象,地址当然不等。6.2 那句"字符串比较永远 equals",今天在项目里报应到眼前了。
连续输错三次:观察"还剩 2 次→还剩 1 次→机会用完"的提示链——验证 i = 2; i >= 0 确实给了三次,不是两次也不是四次。
写 name.equals("admin") 并把 name 初始化为 null 再调用——NullPointerException 现场直播。换成 "admin".equals(name),安安稳稳返回 false。
五、五个常见误区
比地址不比内容,键盘输入永远和字面量不同址——输对了也报错。铁律:字符串比较只用 equals。
name.equals("admin") 在 name 为 null 时直接 NPE。常量/确定非空的一方打头。
i = 2; i >= 0 是三次、i = 3; i > 0 也是三次,但写混了就变两次四次。报错提示先在纸上走一遍再写代码。
打印"登录成功"就 return 完事,CRM 一辈子进不去——门卫放行后必须调用 CustomerView 的入口(TODO 注释先占位)。
"123456" 写死在代码里只配练手。真实系统密码要加密存储、校验走数据库——本阶段知道即可,别把练习习惯带进工作。
六、自测题
== 比地址;键盘输入的字符串和字面量不是同一对象,恒为 false,输对也报错。equals 比内容。
name 可能为 null:前者返回 false 安然无恙,后者抛空指针异常。常量打头防 null。
for (int i = 2; i >= 0; i--)——i 取 2、1、0 共三次,剩余次数提示直接用 i。
main 只看 true/false 结果,不关心验证过程;门卫逻辑改动不影响主菜单——一个方法干一件事。
欢迎语用 System.out.println("欢迎,管理员 " + name);日期可用 6.6 的 LocalDate.now() 打印当天。下一步调用 CustomerView 的主菜单入口(主菜单还没写,先用 TODO 注释占位)。
七、下一步学什么
- ☑ 15.3 客户信息 ✅ 已完成 —— Customer 实体类落地:五字段 private + get/set 官方通道 + 两种构造器,点此复习。
相关笔记
- 第六章 Java API/6.2 字符串类:equals 规矩三连的最终实战岗位
- 第二章 Java编程基础/2.5 循环结构语句:三次机会的 for/while 骨架来源
- 第二章 Java编程基础/2.6 方法:boolean login() 的封装依据
- 第十五章 综合项目:客户关系管理系统/15.1 项目开发准备:启动链与分层蓝图,本节装上第一扇门