目录
1、项目概述
1.1 项目结构
1.2 技术栈
2、核心功能说明
2.1 账户管理
2.2 异常处理体系
3、设计理念解析
3.1 面向对象设计
3.2 关键设计点
4、使用指南
4.1 运行流程
4.2 注意事项
5、扩展建议
5.1增加功能
5.2优化方向
6、主要的功能模块代码说明
6.1exception
6.2main
6.3model
6.4utils
7、项目反思
7.1基础设计思路是通用的
7.2框架层面:这是“玩具级”,但能训练核心思维
7.3结论:这是合格的“起点”
1、项目概述
1.1 项目结构
完整的代码文件包:
说明:bin和src的代码目录是一样的,区别在于java中每一个脚本会对应生成一个类文件XX.class存放在bin目录下,对应到的是scr下的java文件
1.2 技术栈
- 开发环境:Java 17 + Eclipse(其他java编译器都可以)
- 核心技术:面向对象编程、文件序列化、异常处理
- 设计模式:策略模式(通过AccountOperations接口)
2、核心功能说明
2.1 账户管理
2.2 异常处理体系
public class BankException extends Exception { // 自定义异常类public BankException(String message) {super(message);}
}// 使用示例:
try {
account.withdraw(amount);
} catch (BankException e) {System.out.println(e.getMessage()); // 输出"余额不足"等提示
}
3、设计理念解析
3.1 面向对象设计
classDiagramAccount <|-- CheckingAccountAccount <|-- CreditAccountAccount : +String accountNumberAccount : +double balanceAccount : +deposit(double)Account : +withdraw(double)class CheckingAccount {+double checkingBalance}class CreditAccount {+double creditLimit}
3.2 关键设计点
- 抽象与继承:
- Account抽象类实现AccountOperations接口
- CheckingAccount和CreditAccount继承父类并重写核心方法
- 数据持久化:
// 序列化保存
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("accounts.dat"));
oos.writeObject(accounts);// 反序列化加载
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("accounts.dat"));
accounts = (List<Account>) ois.readObject();
3.模块化设计:
module Bank_Management_system { // 模块化封装requires java.base;requires java.desktop;
}
4、使用指南
4.1 运行流程
- 启动程序:运行Main.java
- 主菜单操作:
- 创建账户示例:
// 储蓄账户
new CheckingAccount("C1001", 5000.0);// 信用账户
new CreditAccount("CR2001", 10000.0, 50000.0);
4.2 注意事项
- 账户编号唯一性:系统未做重复校验,需用户自行保证
- 数据安全:accounts.dat文件建议定期备份
- 异常处理:取款操作必须进行try-catch处理
5、扩展建议
5.1增加功能
-
- 账户转账操作
- 交易流水记录
- 利息计算功能
5.2优化方向
-
- 使用数据库替代文件存储
- 增加图形界面(GUI)
- 实现账户编号自动生成
本项目通过约500行代码实现了银行核心业务逻辑,展示了以下软件工程实践:
- 分层架构设计(模型/工具/异常分层)
- 高内聚低耦合原则
- 防御式编程思想
- 数据持久化解决方案
6、主要的功能模块代码说明
Bank_Management_system\bin\com\bank 下主要的功能模块代码说明
6.1exception
--BankException.java
BankException 是一个自定义异常类,继承自 Java 的 Exception,用于在银行系统中封装特定的错误信息(如余额不足、信用额度不足等),并通过构造函数传递错误描述,使异常处理更加清晰和模块化。
package com.bank.exception;import com.bank.model.AccountOperations;
import com.bank.model.Account;
import com.bank.model.CreditAccount;
import com.bank.model.CheckingAccount;import com.bank.utils.AccountData;import com.bank.exception.BankException;public class BankException extends Exception {public BankException(String message) {super(message);}
}
6.2main
--Main.java
Main 类是银行账户管理系统的入口,通过控制台菜单提供账户创建、存款、取款、余额查询等功能,使用 AccountData 类实现账户数据的加载和保存,并通过 Scanner 与用户交互,确保账户操作的完整性和数据的持久化。
package com.bank.main;import com.bank.model.AccountOperations;
import com.bank.model.Account;
import com.bank.model.CreditAccount;
import com.bank.model.CheckingAccount;import com.bank.utils.AccountData;import com.bank.exception.BankException;import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.io.IOException;public class Main {private static List<Account> accounts = new ArrayList<>();public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int choice = -1;try {
accounts = AccountData.loadAccounts(); // 从文件中读取账号列表} catch (Exception e) {System.out.println("未发现账户,请创建账户!");}while (choice != 0) {System.out.println("\n欢迎使用银行账户管理系统!");System.out.println("1. 创建账户");System.out.println("2. 存钱操作");System.out.println("3. 取钱操作");System.out.println("4. 显示余额");System.out.println("0. 退出");System.out.print("请选择你要进行的操作:");
choice = scanner.nextInt();switch (choice) {case 1:createAccount(scanner);break;case 2:processAccount(scanner, true);break;case 3:processAccount(scanner, false);break;case 4:showBalance(scanner);break;case 0:System.out.println("系统退出...");break;default:System.out.println("无效的选择!");}}
scanner.close();try {AccountData.saveAccounts(accounts); // 把当前账号列表存储到文件中} catch (IOException e) {System.out.println("账户存储失败:" + e.getMessage());
e.printStackTrace();}}private static void createAccount(Scanner scanner) {System.out.print("请输入账户类型(1: 储蓄账户,2: 信用卡账户):");int accountType = scanner.nextInt();
scanner.nextLine(); // Consume the newlineSystem.out.print("请输入账户编号:");String accountNumber = scanner.nextLine();if (accountType == 1) {System.out.print("请输入初始余额:");double initialBalance = scanner.nextDouble();Account account = new CheckingAccount(accountNumber, initialBalance);
accounts.add(account);} else if (accountType == 2) {System.out.print("请输入初始余额:");double initialBalance = scanner.nextDouble();System.out.print("请输入信用额度:");double creditLimit = scanner.nextDouble();Account account = new CreditAccount(accountNumber, initialBalance, creditLimit);
accounts.add(account);} else {System.out.println("无效的账户类型!");}}private static void processAccount(Scanner scanner, boolean isDeposit) {System.out.print("请输入账户编号:");String accountNumber = scanner.nextLine();Account account = getAccount(accountNumber);if (account == null) {System.out.println("账户不存在!");return;}if (isDeposit) {System.out.print("请输入存款金额:");double amount = scanner.nextDouble();
account.deposit(amount);} else {System.out.print("请输入取款金额:");double amount = scanner.nextDouble();try {
account.withdraw(amount);} catch (BankException e) {System.out.println(e.getMessage());}}}private static void showBalance(Scanner scanner) {System.out.print("请输入账户编号:");String accountNumber = scanner.nextLine();Account account = getAccount(accountNumber);if (account == null) {System.out.println("账户不存在!");return;}System.out.println("账户余额:" + account.getBalance());}private static Account getAccount(String accountNumber) {for (Account account : accounts) {if (account.getAccountNumber().equals(accountNumber)) {return account;}}return null;}
}
6.3model
--Account.java
Account 是一个抽象类,实现了 AccountOperations 接口并支持序列化,用于定义银行账户的基本结构(如账户号、余额)和核心操作(存款、取款、查询余额),其中取款操作会检查余额并抛出 BankException 异常,确保账户操作的逻辑完整性和安全性。
package com.bank.model;import java.io.Serializable;import com.bank.model.AccountOperations;
import com.bank.model.Account;
import com.bank.model.CreditAccount;
import com.bank.model.CheckingAccount;import com.bank.utils.AccountData;import com.bank.exception.BankException;public abstract class Account implements AccountOperations, Serializable {protected String accountNumber;protected double balance;public Account(String accountNumber) {this.accountNumber = accountNumber;}@Overridepublic void deposit(double amount) {
balance += amount;}@Overridepublic void withdraw(double amount) throws BankException {if (balance < amount) {throw new BankException("余额不足");}
balance -= amount;}@Overridepublic double getBalance() {return balance;}public String getAccountNumber() {return accountNumber;}
}
--AccountOperations.java
AccountOperations 是一个接口,定义了银行账户的核心操作,包括存款 (deposit)、取款 (withdraw) 和查询余额 (getBalance),其中取款操作可能抛出 BankException 异常,确保账户操作的规范性和一致性。
package com.bank.model;import com.bank.model.AccountOperations;
import com.bank.model.Account;
import com.bank.model.CreditAccount;
import com.bank.model.CheckingAccount;import com.bank.utils.AccountData;import com.bank.exception.BankException;public interface AccountOperations {void deposit(double amount);void withdraw(double amount) throws BankException;double getBalance();
}
--CheckingAccount.java
CheckingAccount 是 Account 的子类,专门用于实现储蓄账户的逻辑,重写了存款 (deposit)、取款 (withdraw) 和查询余额 (getBalance) 方法,确保取款时检查存款余额并抛出 BankException 异常,同时通过调用父类方法保持账户余额的一致性。
package com.bank.model;import com.bank.model.AccountOperations;
import com.bank.model.Account;
import com.bank.model.CreditAccount;
import com.bank.model.CheckingAccount;import com.bank.utils.AccountData;import com.bank.exception.BankException;public class CheckingAccount extends Account {// 存款余额private double checkingBalance;public CheckingAccount(String accountNumber, double initialBalance) {super(accountNumber); // 调用父类构造函数,设置账户号码this.checkingBalance = initialBalance;}@Overridepublic void deposit(double amount) {
checkingBalance += amount;super.deposit(amount); // 调用父类的deposit方法,更新balance属性}@Overridepublic void withdraw(double amount) throws BankException {if (amount > checkingBalance) {throw new BankException("存款余额不足");}
checkingBalance -= amount;super.withdraw(amount); // 调用父类的withdraw方法,更新balance属性}@Overridepublic double getBalance() {return checkingBalance; // 返回存款余额}
}
--CreditAccount.java
CreditAccount 是 Account 的子类,专门用于实现信用账户的逻辑,支持信用额度 (creditLimit) 的管理,重写了存款 (deposit)、取款 (withdraw) 和查询余额 (getBalance) 方法,确保取款时检查信用额度并抛出 BankException 异常,同时通过调用父类方法保持账户余额的一致性。
package com.bank.model;import com.bank.model.AccountOperations;
import com.bank.model.Account;
import com.bank.model.CreditAccount;
import com.bank.model.CheckingAccount;import com.bank.utils.AccountData;import com.bank.exception.BankException;public class CreditAccount extends Account {// 信用额度private double creditLimit;public CreditAccount(String accountNumber, double initialBalance, double creditLimit) {super(accountNumber); // 调用父类构造函数,设置账户号码this.creditLimit = creditLimit;// 由于信用账户的初始余额可以是负数,我们直接在父类中设置super.deposit(initialBalance); // 调用父类的deposit方法,设置初始余额}@Overridepublic void deposit(double amount) {
creditLimit += amount;super.deposit(amount); // 调用父类的deposit方法,更新balance属性}@Overridepublic void withdraw(double amount) throws BankException {if (amount > creditLimit) {throw new BankException("信用额度不足");}
creditLimit -= amount;super.withdraw(amount); // 调用父类的withdraw方法,更新balance属性}@Overridepublic double getBalance() {return super.getBalance(); // 返回父类的balance属性}
}
6.4utils
--AccountData.java
AccountData 是一个工具类,负责银行账户数据的持久化操作,通过序列化将账户列表 (List<Account>) 保存到文件 accounts.dat 中,并通过反序列化从文件中加载账户数据,确保账户信息在程序关闭后仍能保留。
package com.bank.utils;import com.bank.model.Account;
import com.bank.model.CheckingAccount;
import com.bank.model.CreditAccount;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;public class AccountData {private static final String ACCOUNTS_FILE = "accounts.dat";public static List<Account> loadAccounts() throws IOException, ClassNotFoundException {List<Account> accounts = new ArrayList<>();try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(ACCOUNTS_FILE))) {
accounts = (List<Account>) ois.readObject();}return accounts;}public static void saveAccounts(List<Account> accounts) throws IOException {try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(ACCOUNTS_FILE))) {
oos.writeObject(accounts);}}
}
7、项目反思
这样比较简易的项目,和实际的开发项目肯定是还差很多,就这个项目来看,里面的设计思路和框架对设计大的项目有用吗?
有用,但需要扩展。
这个简易项目的设计思路对大型项目有帮助,但实际开发需要在此基础上大幅升级。核心价值体现在以下几个方面:
7.1基础设计思路是通用的
- 分层结构:model(数据模型)、utils(工具类)、exception(异常处理)的分包方式,是大型项目模块化的雏形。
- 抽象与继承:用Account抽象类统一管理账户,通过子类实现差异化逻辑,这种面向对象的设计是复杂系统的基础。
- 异常处理机制:自定义BankException的实践,直接对应企业级开发中的错误处理规范。
但问题也很明显:
- 文件存储(accounts.dat)无法支撑高并发和高数据量,实际项目需要用数据库。
- 没有日志、单元测试、接口文档等工程化内容。
- 业务逻辑过于简单(如没有交易流水、权限控制)。
7.2框架层面:这是“玩具级”,但能训练核心思维
- 优势:
- 你实践了分层设计(数据存储、业务逻辑、异常分离),这是微服务架构的基础。
- 通过接口(AccountOperations)定义行为,符合依赖倒置原则,后续可以轻松扩展新账户类型。
- 数据持久化的实现(序列化)帮你理解了状态保存的核心问题。
- 局限:
- 没有引入任何框架(如Spring),实际项目需要依赖注入、AOP等能力。
- 模块化(module-info.java)仅停留在语法层面,未体现真正的模块解耦。
7.3结论:这是合格的“起点”
- 对学习有用:如果你能清晰解释这个项目中为什么用抽象类而不用接口、为什么要序列化、异常为什么要自定义,说明你已经掌握了基础设计思维,也可以熟悉java的代码环境。
- 对实际项目价值有限:就像学会加减法不等于会解微积分,但加减法是必要基础。要应对复杂系统,还需要学习:
- 数据库(MySQL/MongoDB)
- 框架(Spring Boot)
- 设计模式(工厂、策略、观察者等)
- 分布式概念(缓存、消息队列)