15.5 登录管理
把 15.2 的临时工门卫转正——LoginManager 登录管理类。散装 login() 升级后多了三样本事:多账号管理(User[] 货架找人)、登录日志(谁、什么时候登的)、账号锁定(连错 3 次拉闸)。本节真正的收获是认识一个新角色:管理类——只管业务、不装数据,和实体类是平级同事,15.1 的分工口诀到此补全。
一、为什么门卫要"转正"
15.2 的门卫是什么样:login() 一个方法,账号密码写死("admin"/"123456"),只有一个账号,错了三次就三次,谁登过、啥时候登的——一概不记得。练习够用,项目不够格。
真项目里门卫要管一群人:账号得能加、密码得能改(15.4 已经铺了)、老输错的人得拦一拦、出了事得能查"谁几点登过"。这些活一个人干不完,就设个专门的管理岗——LoginManager。
实体类 vs 管理类,这是本节最重要的区分:
| Customer / User(实体类) | LoginManager(管理类) | |
|---|---|---|
| 装什么 | 一个对象的数据 | 一群对象的业务逻辑 |
| 字段 | 姓名、电话…… | 账号数组、失败计数、日志 |
| 方法 | getter/setter、show() | 验证、记账、锁定 |
| 一句话 | 管数据 | 管业务 |
二、核心:LoginManager 三职责
import java.util.Scanner;
public class LoginManager {
private User[] accounts; // 账号货架(2.7 数组 + 15.4 User)
private int userIndex; // 货架已放几个
private int failCount; // 当前账号连错次数
private boolean locked; // 锁定开关
private String lastLoginInfo; // 登录日志(谁 + 何时)
public LoginManager(int capacity) {
accounts = new User[capacity]; // 货架容量定死,够了就翻倍(7 章集合的伏笔)
failCount = 0;
locked = false;
}
// 添加账号:管理类的"行政"活
public void add(User u) {
accounts[userIndex++] = u;
}
// 职责一 + 三:验证 + 锁定
public boolean login(String name, String pwd) {
if (locked) { // 卡口在验证前面!
System.out.println("账号已锁定,请联系管理员。");
return false;
}
for (int i = 0; i < userIndex; i++) {
User u = accounts[i];
if (u.getUsername().equals(name)) { // 6.2 equals:账号也用 equals
if (u.getPassword().equals(pwd)) { // 常量打头不可行时,确保 u 非 null
lastLoginInfo = u.getRealName() + " 登录于 " + System.currentTimeMillis();
failCount = 0; // 登录成功,前账清零
System.out.println("登录成功!" + lastLoginInfo);
return true;
}
failCount++;
if (failCount >= 3) {
locked = true; // 连错 3 次,拉闸
System.out.println("连续错误 3 次,账号已锁定!");
} else {
System.out.println("密码错误,还剩 " + (3 - failCount) + " 次机会");
}
return false;
}
}
System.out.println("账号不存在。");
return false;
}
}
三处讲究:
- 锁定卡口放在验证前面——锁了的账号连"验证密码"的资格都没有,直接拒收。这就是"管理"的分量:不是只会对答案,还会维持秩序。
- 失败计数跟人走:本版计数器挂在管理器上(单账号场景);多账号各自计数时,failCount 应搬进 User 实体——数据跟实体走,逻辑跟管理走,这句是后面所有管理系统的心法。
- 登录成功先清零再放行:
failCount = 0忘了写,改对密码也进不去——计数器状态是管理类的命根。
三、Demo35:两个账号跑一遍
public class Demo35 {
public static void main(String[] args) {
LoginManager lm = new LoginManager(10);
lm.add(new User("admin", "123456", "波仔", "13800000000", "[email protected]"));
lm.add(new User("xiaohong", "abc123", "小红", "13800000002", "[email protected]"));
Scanner sc = new Scanner(System.in);
for (int i = 2; i >= 0; i--) { // 15.2 三次机会骨架照用
System.out.print("用户名:");
String name = sc.next();
System.out.print("密码:");
String pwd = sc.next();
if (lm.login(name, pwd)) {
return; // 放行,进入主菜单
}
}
System.out.println("机会用完,再见。");
}
}
main 里只剩"收证 → 问管理 → 看结果"——验证细节全在 LoginManager 里,main 越来越薄,类越来越专业,这就是 15.2 说的"main 只看结果"进化到项目形态。
用 admin 连续输错密码三次——看到"还剩 2 次→还剩 1 次→账号已锁定"。第四次输对了也进不去:卡口在验证前面,锁定不给面子。
把 System.currentTimeMillis() 换成 6.6 的 LocalDate.now():"登录于 " + LocalDate.now()——时间戳人看不懂,日期人爱看。两个老朋友各干一摊。
给 User 加 private int failCount; 和对应 get/set,管理器里改成 u.setFailCount(u.getFailCount() + 1)——两个账号互不干扰,A 错满 3 次不影响 B。体会"数据跟实体走"。
四、五个常见误区
先验密码再查锁定——锁了的账号输对密码照样进,锁定形同虚设。秩序检查永远排在业务验证前面。
第一次错两次、第二次对——计数器还挂着 2,下次错一次就误锁。成功放行前 failCount = 0 是铁律。
lastLoginInfo = name + " / " + pwd——日志一旦流出,密码全泄。日志记"谁、何时、成败",永远不记密码(15.4 打码规矩的延续)。
比如把"客户信息"也塞进 LoginManager——管理类只管登录这一摊。一摊一个类,15.1 分工口诀不破。
add() 第 11 个账号时 accounts[10] 直接 ArrayIndexOutOfBoundsException(第五章的老朋友)。防御姿势:add 前判断 userIndex >= accounts.length,要么报错要么扩容——扩容就是 7 章集合登场前埋的最后一颗钩子。
五、自测题
实体类(Customer/User)装一个对象的数据,方法以 getter/setter/show 为主;管理类(LoginManager)装一群对象的业务逻辑,字段是数组/计数器/日志,方法是验证/记账/锁定。数据跟实体走,逻辑跟管理走。
锁定的含义是"此账号暂时失去验证资格"——卡口在后,锁定的人输对密码照样进,秩序就没了。
搬进 User 实体——计数是"这个账号"的数据,数据跟实体走;管理器只负责读写它。否则 A 的错误算到 B 头上。
谁(用户名/姓名)、何时(时间戳或日期)、成败。绝不记密码——日志是最容易泄露的地方。
if (userIndex >= accounts.length) { System.out.println("货架已满"); return; } 直接拒收;进阶做法是 new 一个双倍大数组、arraycopy(6.3 老朋友)搬过去再放——这正是 ArrayList 内部干的事,7.1 见。
六、下一步学什么
- ☑ 15.6 本章小结 ✅ 已完成 —— 全章收官 + Java 第 100 篇里程碑,点此复习。
相关笔记
- 第十五章 综合项目:客户关系管理系统/15.2 用户登录:散装门卫的前身,本节给它转正
- 第十五章 综合项目:客户关系管理系统/15.4 个人信息:User 实体类,货架上的"货"
- 第二章 Java编程基础/2.7 数组:User[] 货架的手艺来源
- 第六章 Java API/6.3 System类:currentTimeMillis 记登录时间,秒表再上岗
- 第六章 Java API/6.6 日期和时间类:实验里 LocalDate 上岗,人看得懂的时间