欢迎光临
我们一直在努力

Java面向对象实战 | ATM系统(上)

适合人群:Java初学者、正在学习面向对象和ArrayList的同学 涉及知识点:封装、构造器、ArrayList增删查、Scanner交互、String校验、Random随机卡号 阅读时长:约15分钟 系列文章:本文为ATM系统上篇,下篇将讲解存款、取款、转账等核心业务功能


目录

  • 一、案例需求
  • 二、系统设计分析
  • 三、Account实体类
  • 四、ATM主界面与开户功能
  • 五、用户登录功能
  • 六、运行结果演示
  • 七、知识点总结
  • 八、下篇预告

一、案例需求

开发一个控制台版ATM系统,实现用户的开户、登录以及后续的存取款、转账、改密、注销等完整银行业务功能。

本文(上篇)聚焦于系统框架搭建 + 开户 + 登录三大基础功能。

功能拆解

模块功能本文是否覆盖
系统主界面 登录 / 开户 二选一菜单
用户开户 录入姓名、性别、密码、额度,自动生成卡号
卡号生成 随机8位数字,自动查重
用户登录 卡号校验 + 密码校验
存款 / 取款 核心业务操作 ⏳ 下篇
转账 跨账户资金转移 + 姓氏验证 ⏳ 下篇
密码修改 原密码验证 + 两次确认 ⏳ 下篇
注销账户 余额校验 + 确认注销 ⏳ 下篇

二、系统设计分析

2.1 类设计

本系统采用三层类结构,职责分离:

类名职责核心内容
Account 实体类 封装账户属性:卡号、姓名、性别、密码、余额、额度
ATM 业务类 管理所有账户(ArrayList),实现全部业务逻辑
Test 测试类 创建ATM对象,启动系统

2.2 核心设计要点

  • ArrayList管理账户:系统运行时所有账户存储在 ArrayList<Account> 中,支持动态增删
  • loginAccount记录登录状态:登录成功后,当前账户对象被保存在 loginAccount 字段中,后续操作直接引用
  • getUserName()智能拼接:getter方法中根据性别自动拼接"先生"/“女士”,体现封装的灵活性
  • 卡号自动查重:开户时随机生成8位卡号,与已有卡号比对,确保唯一
  • 2.3 系统流程图

    程序启动


    ┌──────────────┐
    │ ATM主界面 │
    │ 1.用户登录 │
    │ 2.用户开户 │
    └──────┬───────┘

    ┌───┴───┐
    ▼ ▼
    ┌──────┐ ┌──────────┐
    │ 登录 │ │ 开户 │
    │ │ │ 录入信息 │
    │ │ │ 生成卡号 │
    │ │ │ 添加到集合 │
    └──┬───┘ └──────────┘

    │ 登录成功

    ┌──────────────────┐
    │ showAtmCommand() │ ──→ 下篇详解
    │ 业务操作菜单 │
    └──────────────────┘


    三、Account实体类

    3.1 类结构

    Account
    ├── 属性
    │ ├── cardId (String) 卡号
    │ ├── userName (String) 用户名
    │ ├── sex (char) 性别
    │ ├── passWord (String) 密码
    │ ├── money (double) 余额
    │ └── limit (double) 取款额度
    ├── 构造器
    │ ├── 无参构造器
    │ └── 全参构造器
    └── getter/setter
    └── getUserName() → 自动拼接"先生"/"女士"

    3.2 代码实现

    package org.atm;

    public class Account {
    private String cardId;
    private String userName;
    private char sex;
    private String passWord;
    private double money;
    private double limit;

    public Account() {
    }

    public Account(double limit, Double money, String passWord, char sex, String userName, String cardId) {
    this.limit = limit;
    this.money = money;
    this.passWord = passWord;
    this.sex = sex;
    this.userName = userName;
    this.cardId = cardId;
    }

    public String getCardId() {
    return cardId;
    }

    public void setCardId(String cardId) {
    this.cardId = cardId;
    }

    // 重点:根据性别自动拼接"先生"或"女士"
    public String getUserName() {
    return userName + (sex == '男' ? "先生" : "女士");
    }

    public void setUserName(String userName) {
    this.userName = userName;
    }

    public char getSex() {
    return sex;
    }

    public void setSex(char sex) {
    this.sex = sex;
    }

    public String getPassWord() {
    return passWord;
    }

    public void setPassWord(String passWord) {
    this.passWord = passWord;
    }

    public double getMoney() {
    return money;
    }

    public void setMoney(double money) {
    this.money = money;
    }

    public double getLimit() {
    return limit;
    }

    public void setLimit(double limit) {
    this.limit = limit;
    }
    }

    3.3 设计亮点:getUserName() 的巧妙封装

    public String getUserName() {
    return userName + (sex == '男' ? "先生" : "女士");
    }

    小贴士:这里没有直接返回原始 userName,而是在getter中根据 sex 字段动态拼接称呼。这样调用方每次获取用户名都能自动得到"张三先生"、"李四女士"的效果,将业务逻辑封装在实体类内部,避免在每个调用处重复写判断。


    四、ATM主界面与开户功能

    4.1 ATM类成员定义

    package org.atm;

    import java.util.ArrayList;
    import java.util.Random;
    import java.util.Scanner;

    public class ATM {
    // 所有账户集合
    private ArrayList<Account> accounts = new ArrayList<>();
    // 键盘录入
    private Scanner sc = new Scanner(System.in);
    // 当前登录的账户
    private Account loginAccount;

    设计要点:

    • accounts 用ArrayList而非数组,因为账户数量不固定,需要动态增删
    • loginAccount 是关键状态字段,登录后所有操作都围绕它进行
    • Scanner 作为成员变量而非局部变量,避免反复创建

    4.2 start() —— 系统主界面

    public void start() {
    while (true) {
    System.out.println("===欢迎登录ATM系统===");
    System.out.println("1. 用户登录");
    System.out.println("2. 用户开户");
    System.out.println("请选择:");
    String choice = sc.next();
    switch (choice) {
    case "1":
    // 用户登录
    login();
    break;
    case "2":
    // 用户开户
    createAccount();
    break;
    default:
    System.out.println("输入错误,请重新输入!");
    }
    }
    }

    小贴士:主界面用 while(true) 循环,用户操作完毕后自动返回菜单。choice 用 String 类型接收,配合 switch 匹配字符串,避免输入非数字导致程序崩溃。

    4.3 createAccount() —— 用户开户

    /** 开户 */
    private void createAccount() {
    System.out.println("====用户开户操作===");
    Account account = new Account();
    System.out.println("请您输入账号名称:");
    String name = sc.next();
    account.setUserName(name);

    while (true) {
    System.out.println("请您输入您的性别:");
    char sex = sc.next().charAt(0);
    if (sex == '男' || sex == '女') {
    account.setSex(sex);
    break;
    } else {
    System.out.println("您的输入有误,请重新输入!");
    }
    }

    while (true) {
    System.out.println("请您输入账号密码:");
    String password = sc.next();
    System.out.println("请您再次确认账号密码:");
    String okpassword = sc.next();
    if (okpassword.equals(password)) {
    account.setPassWord(password);
    break;
    } else {
    System.out.println("您输入的2次密码不正确,请重新输入!");
    }
    }

    while (true) {
    System.out.println("请设置账号额度:");
    double limit = sc.nextDouble();
    if (limit < 0) {
    System.out.println("设置额度不合理,请重新输入!");
    } else {
    account.setLimit(limit);
    break;
    }
    }

    String newCardId = createCardId();
    account.setCardId(newCardId);
    accounts.add(account);

    System.out.println("恭喜" + account.getUserName() + ",开户成功,卡号是:" + account.getCardId());
    }

    开户流程拆解

    开户开始

    ├─① 输入姓名 ──→ setUserName()

    ├─② 输入性别 ──→ 校验"男"/"女" ──→ setSex()
    │ └─ 校验失败? 重新输入

    ├─③ 输入密码 ──→ 再次确认 ──→ 比对一致? ──→ setPassWord()
    │ └─ 不一致? 重新输入

    ├─④ 设置额度 ──→ 校验≥0 ──→ setLimit()
    │ └─ 不合理? 重新输入

    ├─⑤ 生成卡号 ──→ createCardId() ──→ 自动查重

    └─⑥ 加入集合 ──→ accounts.add(account)
    └─ 输出开户成功信息

    设计要点:每个输入项都用 while(true) 循环 + 校验,确保用户输入合法后才继续。这是控制台程序的常见交互模式。

    4.4 createCardId() —— 随机生成卡号

    /** 创建一个新卡号 */
    private String createCardId() {
    String cardId = "";
    Random r = new Random();
    while (true) {
    for (int i = 0; i < 8; i++) {
    cardId += r.nextInt(10);
    }
    // 判断是否与已有其他卡号重复
    if (hasCardId(cardId) == null) {
    break;
    }
    }
    return cardId;
    }

    小贴士:生成8位随机卡号后,调用 hasCardId() 检查是否与已有卡号冲突。如果重复,while(true) 会重新生成,直到获得唯一卡号。

    4.5 hasCardId() —— 卡号查重

    /** 判断是否已存在卡号 */
    private Account hasCardId(String s) {
    for (int i = 0; i < accounts.size(); i++) {
    Account acc = accounts.get(i);
    if (acc.getCardId().equals(s)) {
    return acc;
    }
    }
    return null;
    }

    复用设计:这个方法返回 Account 对象而非 boolean。找到则返回账户对象(登录时复用),找不到返回 null。一个方法两种用途,是实际开发中常见的设计复用技巧。


    五、用户登录功能

    5.1 login() —— 登录逻辑

    /** 登录账号 */
    private void login() {
    System.out.println("===用户登录操作===");
    if (accounts.size() == 0) {
    System.out.println("当前无任何账户,请先开户再登录!");
    return;
    }
    while (true) {
    System.out.println("请输入您的账号:");
    String id = sc.next();
    Account acc = hasCardId(id);
    if (acc != null) {
    while (true) {
    System.out.println("请您输入账户密码:");
    String password = sc.next();
    if (acc.getPassWord().equals(password)) {
    loginAccount = acc;
    System.out.println(acc.getUserName() + ",欢迎您进入ATM系统");
    showAtmCommand();
    return;
    } else {
    System.out.println("账号密码输入错误,请重新输入!");
    }
    }
    } else {
    System.out.println("账号输入错误!请重新输入");
    }
    }
    }

    5.2 登录流程详解

    登录采用双层 while 循环,外层校验卡号,内层校验密码:

    登录开始

    ├─ 前置检查:accounts为空? → 提示先开户, return

    └─ 外层循环(校验卡号)

    ├─ 输入卡号

    ├─ hasCardId() 查找
    │ │
    │ ├─ 找到 → 进入内层循环(校验密码)
    │ │ │
    │ │ ├─ 输入密码
    │ │ │
    │ │ ├─ 密码正确?
    │ │ │ ├─ 是 → loginAccount = acc
    │ │ │ │ 输出欢迎信息
    │ │ │ │ 调用 showAtmCommand()
    │ │ │ │ return ← 本文到此结束
    │ │ │ │
    │ │ │ └─ 否 → 提示错误, 重新输入密码
    │ │ │
    │ └─ 未找到 → 提示账号错误, 重新输入卡号

    关键点:

  • 复用 hasCardId():登录时用同一个方法查找卡号,找到直接拿到Account对象
  • 密码校验用 equals():String比较必须用 .equals(),不能用 ==
  • “常量”.equals(变量) 防NPE:代码中用 acc.getPassWord().equals(password),因为 acc 确定非null所以安全;如果不确定,建议用 password.equals(acc.getPassWord()) 反转写法
  • 5.3 showAtmCommand() —— 业务菜单入口

    loginAccount = acc;
    System.out.println(acc.getUserName() + ",欢迎您进入ATM系统");
    showAtmCommand();
    return;

    登录成功后,将账户对象保存到 loginAccount,然后调用 showAtmCommand() 进入业务操作菜单。

    showAtmCommand() 内部的存款、取款、转账等完整业务逻辑,将在下篇中详细讲解。


    六、运行结果演示

    6.1 测试类

    package org.atm;

    public class Test {
    public static void main(String[] args) {
    // 1. 创建ATM对象
    ATM atm = new ATM();
    // 2. 启动程序
    atm.start();
    }
    }

    6.2 运行结果 —— 开户流程

    ===欢迎登录ATM系统===
    1. 用户登录
    2. 用户开户
    请选择:
    2
    ====用户开户操作===
    请您输入账号名称:
    张三
    请您输入您的性别:

    请您输入账号密码:
    123456
    请您再次确认账号密码:
    123456
    请设置账号额度:
    5000
    恭喜张三先生,开户成功,卡号是:38472619

    ===欢迎登录ATM系统===
    1. 用户登录
    2. 用户开户
    请选择:
    2
    ====用户开户操作===
    请您输入账号名称:
    李四
    请您输入您的性别:

    请您输入账号密码:
    888888
    请您再次确认账号密码:
    888888
    请设置账号额度:
    10000
    恭喜李四女士,开户成功,卡号是:59103847

    6.3 运行结果 —— 登录流程

    ===欢迎登录ATM系统===
    1. 用户登录
    2. 用户开户
    请选择:
    1
    ===用户登录操作===
    请输入您的账号:
    38472619
    请您输入账户密码:
    123456
    张三先生,欢迎您进入ATM系统

    6.4 运行结果 —— 异常场景

    # 场景1: 无账户时尝试登录
    ===用户登录操作===
    当前无任何账户,请先开户再登录!

    # 场景2: 卡号输入错误
    请输入您的账号:
    99999999
    账号输入错误!请重新输入

    # 场景3: 密码输入错误
    请输入您的账号:
    38472619
    请您输入账户密码:
    wrong
    账号密码输入错误,请重新输入!


    七、知识点总结

    7.1 核心知识点对照表

    知识点本文应用场景关键技巧
    封装 Account类私有属性 + getter/setter getUserName()中嵌入业务逻辑
    ArrayList 存储所有账户对象 add()添加、get()遍历、remove()删除
    Scanner交互 全部用户输入 next()接收字符串、nextDouble()接收小数
    while(true)循环 输入校验 校验通过break,失败继续循环
    方法复用 hasCardId()同时用于开户查重和登录查找 返回Account对象,一法两用
    Random 生成随机8位卡号 nextInt(10)生成0-9随机数字
    String比较 密码校验 equals()方法,非==
    switch匹配String 主菜单选择 case “1” / case “2”

    7.2 OOP设计原则体现

    原则体现
    单一职责 Account只管数据,ATM只管业务,Test只管启动
    封装 所有字段private,外部通过getter/setter访问
    方法复用 hasCardId()在开户和登录中复用

    7.3 易错点提醒

    ⚠️ 注意事项:

  • String比较用equals():acc.getPassWord().equals(password) ✅,acc.getPassWord() == password ❌
  • charAt(0)取首字符:sc.next().charAt(0) 获取输入的第一个字符作为性别
  • 空集合检查:登录前先检查 accounts.size() == 0,避免无意义循环
  • loginAccount状态管理:登录成功后保存当前账户,是后续所有操作的基础

  • 八、下篇预告

    本文实现了ATM系统的框架搭建 + 开户 + 登录功能。下篇将接着 showAtmCommand() 方法,详细讲解:

    功能核心逻辑
    查询账户 展示当前登录账户的完整信息
    存款 金额校验 + 余额累加
    取款 余额校验 + 最小金额限制
    转账 对方账户查找 + 姓氏验证 + 额度限制
    密码修改 原密码验证 + 两次新密码确认
    注销账户 余额清零检查 + 确认注销

    下篇链接:Java面向对象实战 | ATM系统(下)


    如果觉得有帮助,欢迎点赞收藏关注!有问题欢迎评论区交流~

    赞(0)
    未经允许不得转载:171主机测评 » Java面向对象实战 | ATM系统(上)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址