☕ 第十五章 综合项目:客户关系管理系统

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 的老账:

  1. 必须 equals,不能 ==:== 比的是地址。键盘输入的 "admin" 和代码里的字面量 "admin" 不是同一个对象,用 == 永远 false——输对了也报错,最迷惑的新手 Bug。
  2. 常量打头,变量在后:"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)在项目里的样子。

🧪 实验 1:亲眼看 == 翻车

把校验改成 name == "admin"——输对了也报错。键盘输入的字符串和字面量不是同一个对象,地址当然不等。6.2 那句"字符串比较永远 equals",今天在项目里报应到眼前了。

🧪 实验 2:三次输错走全流程

连续输错三次:观察"还剩 2 次→还剩 1 次→机会用完"的提示链——验证 i = 2; i >= 0 确实给了三次,不是两次也不是四次。

🧪 实验 3:变量打头踩空针

写 name.equals("admin") 并把 name 初始化为 null 再调用——NullPointerException 现场直播。换成 "admin".equals(name),安安稳稳返回 false。

五、五个常见误区

⚠️ 误区 1:用 == 比较字符串

比地址不比内容,键盘输入永远和字面量不同址——输对了也报错。铁律:字符串比较只用 equals。

⚠️ 误区 2:变量打头调 equals

name.equals("admin") 在 name 为 null 时直接 NPE。常量/确定非空的一方打头。

⚠️ 误区 3:次数边界靠猜

i = 2; i >= 0 是三次、i = 3; i > 0 也是三次,但写混了就变两次四次。报错提示先在纸上走一遍再写代码。

⚠️ 误区 4:登录成功忘了接主菜单

打印"登录成功"就 return 完事,CRM 一辈子进不去——门卫放行后必须调用 CustomerView 的入口(TODO 注释先占位)。

⚠️ 误区 5:以为明文密码能上生产

"123456" 写死在代码里只配练手。真实系统密码要加密存储、校验走数据库——本阶段知道即可,别把练习习惯带进工作。

六、自测题

❓ Q1:为什么字符串比较必须用 equals?== 会怎样?

== 比地址;键盘输入的字符串和字面量不是同一对象,恒为 false,输对也报错。equals 比内容。

❓ Q2:"admin".equals(name) 为什么比 name.equals("admin") 安全?

name 可能为 null:前者返回 false 安然无恙,后者抛空指针异常。常量打头防 null。

❓ Q3:三次机会的 for 倒序骨架怎么写?

for (int i = 2; i >= 0; i--)——i 取 2、1、0 共三次,剩余次数提示直接用 i。

❓ Q4:把登录封装成 boolean login() 有什么好处?

main 只看 true/false 结果,不关心验证过程;门卫逻辑改动不影响主菜单——一个方法干一件事。

❓ Q5:(毕业题)在 Demo32 基础上加"登录成功后打印欢迎语和当前日期",并说出登录成功后下一步该调用谁。

欢迎语用 System.out.println("欢迎,管理员 " + name);日期可用 6.6 的 LocalDate.now() 打印当天。下一步调用 CustomerView 的主菜单入口(主菜单还没写,先用 TODO 注释占位)。

七、下一步学什么


相关笔记