前言
本次,我将分享自己在学习Java过程中的一个小项目——一个简易的ATM系统。尽管它功能简单且存在诸多可以改进之处,但它承载了我从零开始构建一个小型应用系统的喜悦与挑战。
项目需求
实现简易版的ATM系统功能,包括登录,取款,存款,转账,查询余额,修改密码等功能。
实现步骤
1.数据库设计
设计一张account表,其中包含id(账户),name(姓名),password(密码),money(账户金额)四个属性,id和password用于登录账户
对应entity类:
public class Account {
private Integer id;//账户
private String name;//姓名
private String password;//密码
private BigDecimal money;//账户余额
public Account(Integer id, String name, String password, BigDecimal money) {
this.id = id;
this.name = name;
this.password = password;
this.money = money;
}
public Account() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + ", money=" + money + '}';
}
}
2.各功能实现
2.1登录功能
要求:
输入卡号及密码后,与数据库中信息进行比对,若卡号和密码都匹配则登陆成功,反之提示卡号或密码不正确。
2.2菜单界面
2.3取款功能
要求:
1.取款金额大于0
2.单次取款限制金额最大为20000
3.取款金额不能大于账户余额
2.4存款功能
要求:
1.存款金额大于0
2.单次存款金额上限为50000
2.5转账功能
要求:
1.转账金额大于0
2.不可以向自己转账
3.转账金额不可大于账户余额
4.需要验证密码来确认转账
2.6余额查询功能
要求:输入密码进行验证,密码正确后方可查询余额
2.7修改密码
要求:
1.输入原密码进行验证
2.输入新密码,并再次输入新密码,验证两次密码是否一致
3.编码实现
3.1 代码结构
分别创建entity包,Dao包,service包,util包,以提高代码的复用性,结构如图:
3.2 数据库的连接
在util包中定义Dbutil类,用如下方法实现数据库的连接:
静态初始化块:
1.在类加载时执行一次,读取配置文件db.properties中的数据库连接信息。
2.使用Properties类来加载配置文件,并从中获取数据库驱动、URL、用户名和密码。
获取数据库连接方法:
1.通过调用DriverManager.getConnection(url, user, password)方法来建立与数据库的实际连接。
2.如果成功建立连接,则返回Connection对象;如果失败,则捕获SQLException并打印堆栈跟踪信息,最后返回null。
关闭所有资源:
1.接受三个参数:结果集(ResultSet)、预编译语句(PreparedStatement)和数据库连接(Connection)。
2.检查每个参数是否为null,如果不为null则尝试关闭它们。
3.如果在关闭过程中发生异常,会抛出一个运行时异常
public class Dbutils {
private static String driver;
private static String url;
private static String user;
private static String password;
// 注册驱动
static {
try {
Properties properties = new Properties();
InputStream is = Dbutils.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(is);
is.close();
driver = properties.getProperty("driver");
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 获取连接
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
// 释放资源
public static void closeAll(ResultSet rs, PreparedStatement pstat, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (pstat != null) {
pstat.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
3.3 Dao封装:提高数据操作代码的重用性
在Dao包下创建AccountDao接口
public interface AccountDao {
// 卡号和密码查询
Account selectByIdAndPassword(int id, String password);
// 查询余额
BigDecimal selectMoney(int id);
//更新取款
void updateWithdrawMoney(int id, BigDecimal m);
//更新存款
void updateDepositMoney(int id, BigDecimal m);
//修改密码
void updateChangePwd(int id, String password);
}
在Dao包下建立impl子包,并创建AccountDaoImpl类,用于实现AccountDao接口
public class AccountDaoImpl implements AccountDao {
// 卡号和密码查询
public Account selectByIdAndPassword(int id, String password) {
Connection conn = null;
PreparedStatement pstat = null;
ResultSet rs = null;
try {
conn = Dbutils.getConnection();
if (conn != null) {
pstat = conn.prepareStatement("select id,name,password,money from account where id=? and password=?;");
} else {
System.out.println("数据库连接失败!");
}
pstat.setObject(1, id);
pstat.setObject(2, password);
rs = pstat.executeQuery();
if (rs.next()) {
id = rs.getInt("id");
String name = rs.getString("name");
password = rs.getString("password");
BigDecimal money = rs.getBigDecimal("money");
Account account = new Account(id, name, password, money);
return account;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
Dbutils.closeAll(rs, pstat, conn);
}
return null;
}
// 询余额
public BigDecimal selectMoney(int id) {
Connection conn = null;
PreparedStatement pstat = null;
ResultSet rs = null;
BigDecimal money = null;
try {
conn = Dbutils.getConnection();
if (conn != null) {
pstat = conn.prepareStatement("select money from account where id=?;");
} else {
System.out.println("数据库连接失败!");
}
pstat.setObject(1, id);
rs = pstat.executeQuery();
if (rs.next()) {
money = rs.getBigDecimal("money");
} else {
throw new RuntimeException("查询失败!");
}
return money;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
Dbutils.closeAll(rs, pstat, conn);
}
}
//更新取款
public void updateWithdrawMoney(int id, BigDecimal m) {
Connection conn = null;
PreparedStatement pstat = null;
try {
conn = Dbutils.getConnection();
if (conn != null) {
pstat = conn.prepareStatement("update account set money = money - ? where id = ?;");
} else {
System.out.println("数据库连接失败!");
}
pstat.setObject(1, m);
pstat.setObject(2, id);
pstat.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
Dbutils.closeAll(null, pstat, conn);
}
}
//更新存款
public void updateDepositMoney(int id, BigDecimal m) {
Connection conn = null;
PreparedStatement pstat = null;
try {
conn = Dbutils.getConnection();
if (conn != null) {
pstat = conn.prepareStatement("update account set money = money + ? where id = ?;");
} else {
System.out.println("数据库连接失败!");
}
pstat.setObject(1, m);
pstat.setObject(2, id);
pstat.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
Dbutils.closeAll(null, pstat, conn);
}
}
//修改密码
public void updateChangePwd(int id, String password) {
Connection conn = null;
PreparedStatement pstat = null;
try {
conn = Dbutils.getConnection();
if (conn != null) {
pstat = conn.prepareStatement("update account set password=? where id=?");
} else {
System.err.println("数据库连接失败!");
}
pstat.setObject(1, password);
pstat.setObject(2, id);
pstat.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
Dbutils.closeAll(null, pstat, conn);
}
}
}
3.4 Service封装:提高业务功能代码的重用性
在DService包下创建AccountService接口
public interface AccountService {
// 登录
Account login(int id, String password);
// 取款
void withdrawMoney(int id, BigDecimal m);
// 存款
void depositMoney(int id, BigDecimal m);
// 转账
void transferAccounts(int fromId, int toId, BigDecimal m);
// 修改密码
void changePwd(int id, String password);
// 查询余额
BigDecimal selectBalance(int id);
}
在Service包下建立impl子包,并创建AccountServiceImpl类,用于实现AccountService接口
public class AccountServiceImpl implements AccountService {
// 登录
public Account login(int id, String password) {
AccountDao accountDao = new AccountDaoImpl();
Account account = accountDao.selectByIdAndPassword(id, password);
if (id != account.getId() || !password.equals(account.getPassword())) {
throw new RuntimeException("卡号或密码不正确!");
}
return account;
}
// 取款
public void withdrawMoney(int id, BigDecimal m) {
AccountDao accountDao = new AccountDaoImpl();
// 取款金额必须大于0
if (m.compareTo(new BigDecimal(0)) <= 0) {
throw new RuntimeException("取款金额必须大于零!");
}
// 一次最多取款20000
if (m.compareTo(new BigDecimal("20000")) > 0) {
throw new RuntimeException("单次取款限制两万元!");
}
// 取款数目必须小于等于账户余额
BigDecimal balance = accountDao.selectMoney(id);
if (balance.compareTo(m) > 0) {
accountDao.updateWithdrawMoney(id, m);
System.out.println("取款交易成功!");
} else {
throw new RuntimeException("余额不足,无法完成交易!");
}
}
// 存款
public void depositMoney(int id, BigDecimal m) {
AccountDao accountDao = new AccountDaoImpl();
// 存款金额必须大于0
if (m.compareTo(new BigDecimal(0)) < 0) {
throw new RuntimeException("存款金额必须大于零!");
}
// 一次最多存款50000
if (m.compareTo(new BigDecimal("50000")) > 0) {
throw new RuntimeException("单次存款限制五万元!");
}
accountDao.updateDepositMoney(id, m);
System.out.println("存款交易成功!");
}
// 转账
public void transferAccounts(int fromId, int toId, BigDecimal m) {
AccountDao accountDao = new AccountDaoImpl();
// 转账金额必须大于0
if (m.compareTo(new BigDecimal(0)) <= 0) {
throw new RuntimeException("转账金额必须大于零!");
}
// 不能给自己转账
if (fromId == toId) {
throw new RuntimeException("无法给自己转账!");
}
// 转账数目必须小于等于账户余额
BigDecimal balance = accountDao.selectMoney(fromId);
if (balance.compareTo(m) >= 0) {
accountDao.updateWithdrawMoney(fromId, m);
accountDao.updateDepositMoney(toId, m);
System.out.println("转账交易成功!");
} else {
throw new RuntimeException("余额不足,无法完成交易!");
}
}
// 修改密码
public void changePwd(int id, String password) {
AccountDao accountDao = new AccountDaoImpl();
accountDao.updateChangePwd(id, password);
System.out.println("密码已成功修改!");
}
// 查询余额
public BigDecimal selectBalance(int id) {
AccountDao accountDao = new AccountDaoImpl();
BigDecimal balance = accountDao.selectMoney(id);
System.out.println("查询成功,账户余额为:" + balance + "元");
return null;
}
}
3.5main函数部分
用于菜单选择以及各功能的方法调用
public class ATMSystem {
public static void main(String[] args) {
AccountService accountService = new AccountServiceImpl();
Scanner sc = new Scanner(System.in);
System.out.println("请输入卡号:");
int id = sc.nextInt();
System.out.println("请输入密码:");
String password = sc.next();
try {
boolean flag = true;
Account account = accountService.login(id, password);
System.out.println("登陆成功,欢迎!" + account.getName());
do {
System.out.println("-----1.取款 - 2.存款 - 3.转账 - 4.余额查询 - 5.修改密码 - 0.退出-----");
System.out.println("\t\t\t\t\t" + "请选择您所需要办理的业务:");
int input = sc.nextInt();
switch (input) {
case 1: // 取款
try {
System.out.println("请输入取款金额:");
BigDecimal m = sc.nextBigDecimal();
accountService.withdrawMoney(account.getId(), m);
} catch (Exception e) {
System.err.println(e.getMessage());
}
break;
case 2: // 存款
try {
System.out.println("请输入存款金额:");
BigDecimal m = sc.nextBigDecimal();
accountService.depositMoney(account.getId(), m);
} catch (Exception e) {
System.err.println(e.getMessage());
}
break;
case 3: // 转账
try {
System.out.println("请输入收款方卡号:");
int toId = sc.nextInt();
System.out.println("请输入转账金额:");
BigDecimal amount = sc.nextBigDecimal();
System.out.println("请输入密码进行验证!");
String pwd1 = sc.next();
if (pwd1.equals(account.getPassword())) {
accountService.transferAccounts(id, toId, amount);
} else {
throw new RuntimeException("密码输入错误,验证失败!");
}
} catch (RuntimeException e) {
System.err.println(e.getMessage());
}
break;
case 4: // 余额查询
try {
System.out.println("请输入密码进行验证!");
String pwd2 = sc.next();
if (pwd2.equals(account.getPassword())) {
accountService.selectBalance(id);
} else {
throw new RuntimeException("密码输入错误,验证失败!");
}
} catch (RuntimeException e) {
System.err.println(e.getMessage());
}
break;
case 5: // 修改密码
try {
// 输入原密码进行验证
System.out.println("请输入原密码进行验证!");
String oldPwd = sc.next();
if (!oldPwd.equals(account.getPassword())) {
throw new RuntimeException("原密码输入不正确!");
}
System.out.println("请输入新密码:");
String newPwd = sc.next();
System.out.println("请再次输入新密码:");
String reNewPwd = sc.next();
//比较两次输入新密码的一致性
if (newPwd.equals(reNewPwd)) {
accountService.changePwd(id, newPwd);
} else {
throw new RuntimeException("两次密码输入不一致!");
}
} catch (RuntimeException e) {
System.err.println(e.getMessage());
}
break;
case 0: // 退出系统
flag = false;
break;
default:// 其他错误输入处理
System.out.println("请按正确格式重新输入!");
break;
}
} while (flag);
System.out.println("已退出,欢迎下次使用!");
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
结语
通过这篇博客,我希望能够梳理自己的学习路径,同时记录自己的学习历程。该ATM系统功能有限,仍需优化,仅供参考!