掌握 三层架构设计:view/controller(视图或控制器层) + mapper(数据访问层) + service(业务逻辑层)
实现 纯内存存储(使用数组模拟数据库),理解无持久化场景下的数据管理
理解 事务回滚的模拟机制 在转账中的作用(通过状态回退实现)
能够设计 菜单驱动的控制台应用程序
实践 封装与模块化编程,提升代码可维护性
构建一个命令行界面(CLI)的简易银行系统,实现账户创建、登录、余额查询和转账功能
输入:
用户名(非空、唯一)
密码(非空,明文存储)
处理逻辑:
系统自动分配账号(从 1001 开始递增)
初始余额固定为 1000 元
若用户名已存在,拒绝创建
系统最多支持 5 个账户(数组容量限制)
输出:
成功:提示“账户创建成功!账号:XXXX”
失败:提示“用户名已存在” 或 “系统已满”
输入:
用户名
密码
验证规则:
用户名必须存在
密码必须完全匹配(区分大小写)
输出:
成功:进入已登录菜单
失败:提示“用户名或密码错误!”
触发条件:用户登录后选择“查询余额”
显示格式:
-------------------------------------------------
账号 客户姓名 余额
1001 张三 1000
-------------------------------------------------要求:实时显示当前账户信息
输入:
收款人账号(整数)
转账金额(正整数)
校验逻辑(顺序执行,模拟事务):
转账金额 > 0
转出账户余额 ≥ 转账金额
收款账号存在(在系统中)
执行逻辑:
若所有校验通过,则 同时更新 转出方与收款方余额
否则,不执行任何修改(即“回滚”效果)
输出:
成功:“转账成功!”
失败:对应错误提示(如“余额不足”、“收款账号不存在”等)
注意:由于是单线程程序,无需锁机制;通过“先校验、再一次性修改”即可保证原子性
启动程序
│
├─→ [1] 创建账户 → 输入用户名/密码 → 成功/失败提示
│
├─→ [2] 登录 → 输入凭据 →
│ │
│ └─→ 成功 → 进入子菜单:
│ ├─ [1] 转账
│ ├─ [2] 查余额
│ └─ [3] 退出登录
│
└─→ [3] 退出系统 → 程序终止MiniBankingApp/
├── src/
│ └── banking/
│ ├── entity/ // 实体类
│ │ └── Account.java
│ ├── mapper/ // 数据访问层(内存模拟)
│ │ └── AccountMapper.java
│ ├── service/ // 业务逻辑层
│ │ └── BankService.java
│ ├── views/ // 视图层(UI 控制台交互)
│ │ └── BankView.java
│ └── Main.java // 程序入口
└──包命名规范清晰体现分层职责,符合现代 Java 工程实践
Entity
package banking.entity;
public class Account {
private int accountNo;
private String username;
private String password; // 实际应加密,此处简化
private int balance;
public Account(int accountNo, String username, String password) {
this.accountNo = accountNo;
this.username = username;
this.password = password;
this.balance = 1000; // 初始余额
}
public int getAccountNo() {
return accountNo;
}
public void setAccountNo(int accountNo) {
this.accountNo = accountNo;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}Mapper
package banking.mapper;
import banking.entity.Account;
public class AccountMapper {
// 使用固定长度为 5 的数组存储账户
private static final Account[] ACCOUNTS = new Account[5];
private static int nextAccountNo = 1001; // 起始账号
// 获取下一个可用账号
public static int getNextAccountNo() {
return nextAccountNo++;
}
// 检查用户名是否已存在
private static boolean usernameExists(String username) {
for (Account acc : ACCOUNTS) {
if (acc != null && acc
.getUsername()
.equals(username)) {
return true;
}
}
return false;
}
// 查找第一个空位(用于插入新账户)
private static int findEmptySlot() {
for (int i = 0; i < ACCOUNTS.length; i++) {
if (ACCOUNTS[i] == null) {
return i;
}
}
return -1; // 数组已满
}
// 保存新账户(模拟“插入”)
public static boolean save(Account account) {
if (usernameExists(account.getUsername())) {
return false; // 用户名重复
}
int slot = findEmptySlot();
if (slot == -1) {
System.out.println("系统已达到最大账户数量(5个),无法创建新账户!");
return false;
}
ACCOUNTS[slot] = account;
return true;
}
// 按用户名查找账户
public static Account findByUsername(String username) {
for (Account acc : ACCOUNTS) {
if (acc != null && acc
.getUsername()
.equals(username)) {
return acc;
}
}
return null;
}
// 按账号查找账户
public static Account findByAccountNo(int accountNo) {
for (Account acc : ACCOUNTS) {
if (acc != null && acc.getAccountNo() == accountNo) {
return acc;
}
}
return null;
}
// (可选)获取当前账户总数,用于调试
public static int getAccountCount() {
int count = 0;
for (Account acc : ACCOUNTS) {
if (acc != null) count++;
}
return count;
}
}Service
package banking.service;
import banking.entity.Account;
import banking.mapper.AccountMapper;
public class BankService {
public static void createAccount(String username, String password) {
if (username == null || username
.trim()
.isEmpty() ||
password == null || password
.trim()
.isEmpty()) {
System.out.println("用户名和密码不能为空!");
return;
}
Account newAccount = new Account(AccountMapper.getNextAccountNo(), username, password);
if (AccountMapper.save(newAccount)) {
System.out.println("账户创建成功!账号:" + newAccount.getAccountNo());
} else {
System.out.println("用户名已存在,请更换用户名。");
}
}
public static Account login(String username, String password) {
Account account = AccountMapper.findByUsername(username);
if (account != null && account
.getPassword()
.equals(password)) {
return account;
}
return null;
}
public static void showBalance(Account account) {
System.out.println("\n-------------------------------------------------");
System.out.printf("%s %15s %12s\n", "账号", "客户姓名", "余额");
System.out.printf("%s %16s %15s\n",
account.getAccountNo(),
account.getUsername(),
account.getBalance());
System.out.println("-------------------------------------------------");
}
// 模拟事务:先检查,再执行,失败则不修改(因无并发,直接顺序操作即可)
public static void transfer(Account sender, int receiverAccountNo, int amount) {
if (amount <= 0) {
System.out.println("转账金额必须大于 0!");
return;
}
if (sender.getBalance() < amount) {
System.out.println("余额不足!");
return;
}
Account receiver = AccountMapper.findByAccountNo(receiverAccountNo);
if (receiver == null) {
System.out.println("收款账号不存在!");
return;
}
// 执行转账(原子操作模拟)
sender.setBalance(sender.getBalance() - amount);
receiver.setBalance(receiver.getBalance() + amount);
System.out.println("转账成功!");
}
}package banking.views;
import banking.entity.Account;
import banking.service.BankService;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class BankView {
private static final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in));
public static void launch() throws Exception {
while (true) {
System.out.println("\n===============================");
System.out.println(" 欢迎使用 InBank 银行系统");
System.out.println("===============================");
System.out.println("1) 创建账户");
System.out.println("2) 登录账户");
System.out.println("3) 退出系统");
System.out.print("请选择操作编号:");
int choice = Integer.parseInt(READER.readLine());
switch (choice) {
case 1 -> handleCreateAccount();
case 2 -> handleLogin();
case 3 -> {
System.out.println("感谢使用 InBank 银行系统!再见。");
System.exit(0);
}
default -> System.out.println("无效选项!请输入 1、2 或 3。");
}
}
}
private static void handleCreateAccount() throws Exception {
System.out.print("请输入唯一用户名:");
String username = READER.readLine();
System.out.print("请输入密码:");
String password = READER.readLine();
BankService.createAccount(username, password);
}
private static void handleLogin() throws Exception {
System.out.print("请输入用户名:");
String username = READER.readLine();
System.out.print("请输入密码:");
String password = READER.readLine();
Account account = BankService.login(username, password);
if (account == null) {
System.out.println("登录失败:用户名或密码错误!");
return;
}
loggedInMenu(account);
}
private static void loggedInMenu(Account account) throws Exception {
while (true) {
System.out.println("\n您好," + account.getUsername() + "!请选择操作:");
System.out.println("1) 转账");
System.out.println("2) 查询余额");
System.out.println("3) 退出登录");
System.out.print("请输入选项编号:");
int choice = Integer.parseInt(READER.readLine());
switch (choice) {
case 1 -> {
System.out.print("请输入收款人账号:");
int receiverAc = Integer.parseInt(READER.readLine());
System.out.print("请输入转账金额:");
int amount = Integer.parseInt(READER.readLine());
BankService.transfer(account, receiverAc, amount);
}
case 2 -> BankService.showBalance(account);
case 3 -> {
System.out.println("已退出登录。");
return;
}
default -> System.out.println("无效选项!");
}
}
}
}package banking;
import banking.views.BankView;
public class Main {
public static void main(String[] args) throws Exception {
BankView.launch();
}
}三层架构清晰分离关注点:entity 定义数据结构,mapper 管理数据存取,service 封装业务规则。
内存模拟数据库 是理解 CRUD 和事务逻辑的有效教学手段。
控制台交互 虽简单,但完整体现了用户流程闭环。
事务原子性 在无数据库时可通过“预检查 + 顺序执行”保证(高并发需额外机制)。