当前位置: 首页 > news >正文

基于 JavaSE 实现(GUI)的 小型ATM 银行模拟系统

基于 JavaSE 的 ATM 银行系统

1 前言

该软件主要是利用 JavaSE 的 I/O 流进行文件读写设计开发一个小型的银行模拟系统,菜单面板具有七大功能:开户、存款、取款、挂失、查询、改密、转账,其实还有一些工具类功能:实时日期显示、MD5 加密(暂未实现),操作界面要符合用户的一般习惯。由于 JavaSwing 原生的样式不是很美观,所以加入了 UI,引用了一个开源的 UI 依赖库。

2 需求分析

2.1 要求

设计类结构,模拟 ATM 银行系统

2.2 任务

(1)开户

(2)存/取款

(3)转账

(4)登陆

(5)挂失

(6)查询

(7)改密

(8)显示当前时间

2.3 运行环境

(1)WIN10 系统

(2)JDK1.8

2.4 开发工具

Java 语言

3 分析和设计

3.1 功能分析说明图及设计思路

本系统设计使用了分层的设计架构思想,主要分为了 Service 业务逻辑层和 Dao 数据访问层。区分层次的目的即为了“高内聚,低耦合"的思想。

1、业务逻辑层(Service):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。

2、数据访问层(Dao):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等。

优点:

1、开发人员可以只关注整个结构中的其中某一层;

2、可以很容易的用新的实现来替换原有层次的实现;

3、可以降低层与层之间的依赖;

4、有利于标准化;

5、利于各层逻辑的复用;

6、扩展性强。

7、安全性高。用户端只能通过逻辑层来访问数据层,减少了入口点,把很多危险的系统功能都屏蔽了;

8、项目结构更清楚,分工更明确,有利于后期的维护和迭代,更容易的实现后期将文件操作改成对数据库的操作。

3.2 各项功能说明

(1)开户:这部分的功能主要包括用户信息的录入,姓名,身份证号,账户名,密码开户金额等信息,而且对需要录入的信息通过 CheckUtils 工具类进行了一个非法校验,比如真实姓名只能为中文,账户名、密码不能含有中文,身份证号限定为 18 位,开户金额必须大于 0,且对用户名进行一个重复校验,若已有数据中已存在的用户名,不能进行重复开户,校验完成后通过 UserDao 类将开户信息用文件操作存入 Message.txt 文件中。

(2)登陆:使用开户、存款、取款、挂失、查询、改密、转账功能前都需要进行登陆的一个校验,通过 CheckUtils 先进行一个数据是否合法的校验,再进行登陆信息与 Message.txt 文本数据的一个匹配,匹配成功则放行

(3)查询:进入该项功能界面后,用户可以查询到个人的真实姓名、身份证号和账户余额。

(4)存款:进入存款界面,系统将提示输入所需要账户所需要存入的金额,这里还是使用了 CheckUtils 的 checkMoney 方法对存入数据进行一个校验,不能存入负数。通过 UserDao 的 updateMoney 方法传递参数,通过 IO 流读取并更改金额信息,存入成功后会回显当前余额。这个功能在一开始没有考虑到存入金额在实际可能会超出整形范围,在输入超过整形数据时会报错,后面进行了修改,将 int 类型改成了 Long。

(5)取款:进入取款界面,原理同功能(4)存款,不同的是其中 updateMoney 方法的参数传的参数前面加上一个负号,就相当于取款了。并且对取出金额进行了一个判断,若取出金额大于原有金额,取款失败。

(6)转账:登陆成功后,输入对方账号和转出金额,就能完成转账功能。这个功能是存款和取款的综合,在设计的时候调用的同样是 UserDao 中的 updateMoney 方法,转账方进行取款操作,被转账方进行存款操作。

(7)改密:登陆成功后可以进行改密操作,用到 UserMessageUtils 中的 updatePassword 方法,其实 UserDao 中的方法都差不多,只不过针对操作的数据对象不同。(后面对 UserDao 的方法进行了 BaseCode 抽取,抽取出了公共的方法,如 CheckUtils 中大多数的 check 方法都调用了这个 UserDao 的 queryMessage,queryMessage 这个方法设计了有 accoutName,password,userId,realName,flag 五个参数,flag 参数作为一个标记,如 CheckUtils 的 registerCheck 方法只有一个有意义的参数 accountName,那么我们就调用 queryMessage,flag 传 1,除了 accountName 其他传 null,实现的代码的可重用)

(8)挂失:通过真实姓名、身份证号和账户可以找回密码,虽然与现实实际有点不符合。(可以使用 MD5 加密改善,但是程序使用了 MD5 加密后是无法找回原来密码的,回显的是 MD5 加密后的密文。)

(9)日期实时显示:在菜单面板上添加一个标签,调用 Java 的一个 Time 类设置 1m 实现一个动作,实际上是一个线程。

(10)退出系统,结束任务。

3.3 主要类图

主要类图

4 具体代码实现

该程序源码比较长,所以下面只放出部分类

4.1Login 类源码:

package com.novo.service;import com.novo.utils.CheckUtils;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;/*** @author zhuweixiong* @date 2021/10/16-16:55*/
public class Login extends JFrame implements ActionListener {JButton jb1, jb2;  //按钮JPanel jp1, jp2, jp3;        //面板JTextField jtf;   //文本框JLabel jlb1, jlb2; //标签JPasswordField jpf; //密码框//调用login后执行什么操作String order;public Login(String order) {this.order = order;jb1 = new JButton("登录");jb2 = new JButton("重置");//设置按钮监听jb1.addActionListener(this);jb2.addActionListener(this);jp1 = new JPanel();  //创建面板jp2 = new JPanel();jp3 = new JPanel();jlb1 = new JLabel("账号");  //添加标签jlb2 = new JLabel("密码");jtf = new JTextField(10);    //创建文本框和密码框jpf = new JPasswordField(10);//加入面板中jp1.add(jlb1);jp1.add(jtf);jp2.add(jlb2);jp2.add(jpf);jp3.add(jb1);jp3.add(jb2);//将JPane加入JFrame中this.add(jp1);this.add(jp2);this.add(jp3);//设置布局this.setTitle("用户登录");this.setLayout(new GridLayout(3, 1));this.setSize(300, 200);   //设置窗体大小this.setLocationRelativeTo(null);//在屏幕中间显示(居中显示)this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);  //设置仅关闭当前窗口this.setVisible(true);  //设置可见this.setResizable(false);    //设置不可拉伸大小}@Overridepublic void actionPerformed(ActionEvent e) {if (e.getActionCommand().equals("登录")) {try {login();} catch (Exception e1) {e1.printStackTrace();}} else if (e.getActionCommand().equals("重置")) {clean();}}//清空账号和密码框private void clean() {jtf.setText("");    //设置为空jpf.setText("");}//验证登录信息,并做处理public void login() throws HeadlessException, IOException {//判断账户名和密码是否包含中文if (CheckUtils.isExistChinese(jtf.getText()) || CheckUtils.isExistChinese(jpf.getText())) {JOptionPane.showMessageDialog(null, "用户名或密码存在中文,不合法!", "消息提示", JOptionPane.WARNING_MESSAGE);} else if (jtf.getText().isEmpty() && jpf.getText().isEmpty()) {JOptionPane.showMessageDialog(null, "账号密码为空,请输入!", "消息提示", JOptionPane.WARNING_MESSAGE);} else if (jtf.getText().isEmpty()) {JOptionPane.showMessageDialog(null, "账号为空,请输入!", "消息提示", JOptionPane.WARNING_MESSAGE);} else if (jpf.getText().isEmpty()) {JOptionPane.showMessageDialog(null, "密码为空,请输入!", "消息提示", JOptionPane.WARNING_MESSAGE);} else if (CheckUtils.loginCheck(jtf.getText(), jpf.getText())) {JOptionPane.showMessageDialog(null, "登录成功!", "提示消息", JOptionPane.INFORMATION_MESSAGE);//dispose();  //使文原窗体消失if (order.equals("查询")) {//携带accountName跳转到登录个人信息选项界面new QueryMessage(jtf.getText());dispose();}if (order.equals("存款")) {new SaveMoney(jtf.getText());dispose();}if (order.equals("取款")) {new DrawMoney(jtf.getText());dispose();}if (order.equals("改密")) {new ChangePassword(jtf.getText());dispose();}if (order.equals("转账")) {new Transfer(jtf.getText());dispose();}} else {JOptionPane.showMessageDialog(null, "账号密码错误请重新输入!", "消息提示", JOptionPane.ERROR_MESSAGE);clean();  //调用清除函数}}
}

4.2SaveMoney 类源码:

package com.novo.service;import com.novo.dao.UserDao;
import com.novo.utils.CheckUtils;import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;/*** @author zhuweixiong* @date 2021/10/16-15:55*/
public class SaveMoney extends JFrame implements ActionListener {String accountName;JButton jb1, jb2;  //按钮JLabel jlb1; //标签JTextArea jta1;public SaveMoney(String accountName) {this.accountName = accountName;jb1 = new JButton("确定");jb2 = new JButton("重置");//设置按钮监听jb1.addActionListener(this);jb2.addActionListener(this);jlb1 = new JLabel("请输入存入金额:");  //添加标签//创建文本框jta1 = new JTextArea();//设置布局this.setTitle("存款");this.setLayout(null);this.setSize(300, 300);//存入标签和文本框jlb1.setBounds(5, 20, 200, 20);jta1.setBounds(20, 50, 250, 50);jta1.setFont(new java.awt.Font("Dialog", 0, 15)); //设置字体为字形, 不加粗,15号字体//确定和重置按钮jb1.setBounds(60, 120, 62, 28);jb2.setBounds(160, 120, 62, 28);this.add(jlb1);this.add(jta1);this.add(jb1);this.add(jb2);this.setLocationRelativeTo(null);//在屏幕中间显示(居中显示)this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);  //设置仅关闭当前窗口this.setVisible(true);  //设置可见this.setResizable(false);    //设置不可拉伸大小}//清空账号和密码框private void clean() {jta1.setText("");    //设置为空}@Overridepublic void actionPerformed(ActionEvent e) {if (e.getActionCommand().equals("确定")) {try {saveMoney();   //将存入金额传入判断是否合法} catch (IOException e1) {e1.printStackTrace();}} else if (e.getActionCommand().equals("重置")) {clean();}}private void saveMoney() throws IOException {if (jta1.getText().isEmpty()) {JOptionPane.showMessageDialog(null, "金额为空,请重新输入!", "消息提示", JOptionPane.WARNING_MESSAGE);} else if (CheckUtils.checkMoney(jta1.getText())) {//验证金额是否合法//将账户和金额传入, 进行存储String newMoney = UserDao.updateMoney(accountName, Integer.parseInt(jta1.getText()));if (!newMoney.equals("负数")) {JOptionPane.showMessageDialog(null, "成功存款:¥"+jta1.getText()+"\n剩余余额为:¥"+newMoney, "消息提示", JOptionPane.INFORMATION_MESSAGE);}} else {JOptionPane.showMessageDialog(null, "存入金额不合法!", "消息提示", JOptionPane.WARNING_MESSAGE);}}
}

4.3UserDao 类源码:

package com.novo.dao;import java.io.*;/*** @author zhuweixiong* @date 2021/10/31-9:39* 新增了一个Dao类 从Utils抽取了公共的Base* 用于数据交互*/
public class UserDao {public static Object queryMessage(String accountName, String password, String realName, String userId, int flag) throws IOException {File file = new File("Message.txt");   //创建文件类if (!file.exists() || file.isDirectory()) //判断文件是否存在file.createNewFile();BufferedReader br = new BufferedReader(new FileReader(file)); //创建读入缓冲流,按行读入String line;while ((line = br.readLine()) != null) {String sbstring = line;   //转化为stringint n = sbstring.length();            //测字符串长度String[] message = new String[5];     //按~拆分 成5个字符串数组,按账号和密码进行信息验证int k = 0;for (int i = 0; i < 5; i++) message[i] = "";//我们在写入账户时用~分割, 所以我们利用~在分割开来for (int i = 0; i < n; i++) {if (sbstring.charAt(i) == '~') {//System.out.println("@"+message[k]);k++;} else {message[k] += sbstring.charAt(i);}}switch (flag) {case 1:if (accountName.equals(message[2])) return true;break;case 2:if (accountName.equals(message[2]) && password.equals(message[3])) return true;break;case 3:if (realName.equals(message[0]) && userId.equals(message[1]) && accountName.equals(message[2]))return message[3];break;default:break;}}return false;}/** 将注册的信息写入文本*/public static void write(String[] message) throws IOException {File file = new File("Message.txt");String messagesum = "";for (int i = 0; i < 5; i++)  //将信息格式化存储messagesum += message[i] + "~";if (!file.exists())file.createNewFile();FileOutputStream fos = new FileOutputStream(file, true); //建立输出对象,true表示追加StringBuffer sb = new StringBuffer();      //创建字符串流sb.append(messagesum + "\n");                //向字符串流中添加信息fos.write(sb.toString().getBytes("UTF-8"));         //将字符串流中的信息写入文本fos.close();            //关闭}/**   读取信息,将用户名信息返回(如果不存在返回null),和Check类配合使用*/public static String[] read(String accountName) throws IOException {File file = new File("Message.txt");if (!file.exists() || file.isDirectory())throw new FileNotFoundException();BufferedReader br = new BufferedReader(new FileReader(file));String line = "";String[] message = new String[5];     //按~拆分 成5个字符串数组,按账号和密码进行信息验证while ((line = br.readLine()) != null) {for (int i = 0; i < 5; i++)message[i] = "";int k = 0;for (int i = 0; i < line.length(); i++) {if (line.charAt(i) == '~') {//System.out.println("@"+message[k]);k++;} else {message[k] += line.charAt(i);}}if (message[2].equals(accountName))  //返回找到用户的信息{return message;}}return null;}/*在存款取款操作 时 更新金额**/public static String updateMoney(String accountName, long money) throws IOException {File file = new File("Message.txt");if (!file.exists() || file.isDirectory())throw new FileNotFoundException();//读文件BufferedReader br = new BufferedReader(new FileReader(file));String line;StringBuffer sb = new StringBuffer();//建立字符串流String moneyString = "";String[] message = new String[5];     //按~拆分 成5个字符串数组,按账号和密码进行信息验证while ((line = br.readLine()) != null) {String sbstring = line;int n = sbstring.length();            //测字符串长度for (int i = 0; i < 5; i++)message[i] = "";int k = 0;for (int i = 0; i < n; i++) {if (sbstring.charAt(i) == '~') {//System.out.println("@"+message[k]);k++;} else {message[k] += sbstring.charAt(i);}}//找到该账户名if (message[2].equals(accountName)) {String newMessage = "";long moneyLong;moneyLong = Long.parseLong(message[4]) + money;  //金额转为int操作//原金额                                   //存入金额if (moneyLong < 0) {return "负数";}moneyString = String.valueOf(moneyLong);    //将long转为stringfor (int i = 0; i < 4; i++)                //转化为规定格式文件newMessage += message[i] + "~";newMessage += moneyString + "~";sb.append(newMessage + "\n");} else {sb.append(line + "\n");}}/** 说明:* 本来的想法是在原文件对象中覆盖内容,但是发现覆盖后文本为空, 无法解决* 但重新创建文件对象,则可以完成操作*/File file1 = new File("Message.txt");   //重新建立文件对象, 覆盖写入文本if (!file1.exists())file1.createNewFile();FileOutputStream out = new FileOutputStream(file1, false);  //false为重写操作out.write(sb.toString().getBytes("UTF-8"));out.close();return moneyString;}//更新密码public static String updatePassword(String accountName, String pwd) throws IOException {File file = new File("Message.txt");if (!file.exists() || file.isDirectory())throw new FileNotFoundException();//读文件BufferedReader br = new BufferedReader(new FileReader(file));String line;//写文件//FileOutputStream out=new FileOutputStream(file,false);StringBuffer sb = new StringBuffer();String moneystring = "";String[] message = new String[5];     //按~拆分 成5个字符串数组,按账号和密码进行信息验证while ((line = br.readLine()) != null) {String sbstring = line;int n = sbstring.length();            //测字符串长度for (int i = 0; i < 5; i++)message[i] = "";int k = 0;for (int i = 0; i < n; i++) {if (sbstring.charAt(i) == '~') {k++;} else {message[k] += sbstring.charAt(i);}}//找到该账户名if (message[2].equals(accountName)) {//修改密码 循环只读到[2] 将新的password添加到[3]for (int i = 0; i < 3; i++)sb.append(message[i] + "~");sb.append(pwd + "~");sb.append(message[4] + "~\n");} else {sb.append(line + "\n");}}/** 说明:* 本来的想法是在原文件对象中覆盖内容,但是发现覆盖后文本为空, 无法解决* 但重新创建文件对象,则可以完成操作*/File file1 = new File("Message.txt");if (!file1.exists())file1.createNewFile();FileOutputStream out = new FileOutputStream(file1, false);out.write(sb.toString().getBytes("UTF-8"));out.close();return moneystring;}}

4.4CheckUtils 类源码:

package com.novo.utils;import com.novo.dao.UserDao;import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @author zhuweixiong* @date 2021/10/16-15:55*/
public class CheckUtils {/*** 登陆校验* @param accountName* @param password* @return* @throws IOException*/public static boolean loginCheck(String accountName, String password) throws IOException {return (Boolean) UserDao.queryMessage(accountName, password, null, null, 2);}/*** 注册校验 校验账户名是否已经存在* @param accountName* @return* @throws IOException*/public static boolean registerCheck(String accountName) throws IOException {return (Boolean) UserDao.queryMessage(accountName, null, null, null, 1);}/*** 挂失校验* 找回密码需要验证, 姓名,身份证号,和账户是否与注册时保持一致* @param realName* @param userId* @param accountName* @return* @throws IOException*/public static String reportLoseCheck(String realName, String userId, String accountName) throws IOException {Object obj = UserDao.queryMessage(accountName, null, realName, userId, 3);if (obj instanceof String) return (String) obj;else return null;}/*** 校验金额是否为合法数字* @param money* @return*/public static boolean checkMoney(String money) {for (int i = 0; i < money.length(); i++) {if (money.charAt(i) < '0' || money.charAt(i) > '9') {return false;}}return true;}/*** 验证用户名和密码是否存在中文* @param str* @return*/public static boolean isExistChinese(String str) {Pattern p = Pattern.compile("[\u4e00-\u9fa5]");Matcher m = p.matcher(str);if (m.find()) {return true;}return false;}/*** 验证真实姓名是否为全中文* @param realName* @return*/public static boolean isAllChinese(String realName) {int n = 0;for (int i = 0; i < realName.length(); i++) {n = (int) realName.charAt(i);//unicode编码范围://汉字:[0x4e00,0x9fa5](或十进制[19968,40869])if (!(19968 <= n && n < 40869)) {return false;}}return true;}
}

5 课程设计总结

5.1 程序运行结果或预期运行结果

运行程序,首先进入菜单。

菜单界面: 登陆界面:

改密界面: 查询界面:

存取款界面: 挂失界面:

转账界面:

5.2 设计总结

开户界面:

通过这次面向对象程序设计课程设计,使我加深对类与对象的理解,使我能将学到的面向对象的程序设计思想用到具体的工作和学习中。通过课程设计,实现这个 ATM 系统功能对于我来说实际上不难,但是我对 JavaSESwing 图形开发并不熟悉,虽然说用 JavaSwing 进行界面开发这项技术在现在已经被淘汰掉了,但是自己通过这次作业还是对 Swing 开发加深了理解。

致 谢

这次课程设计能够顺利完成,首先非常感谢我们的指导老师邓晓曦,是老师传授我知识,教会我编程的方法和思路,还有感谢一些在我有困难时给予我帮助的同学。

http://www.xdnf.cn/news/900595.html

相关文章:

  • [特殊字符]解决 “IDEA 登录失败。不支持早于 14.0 的 GitLab 版本” 问题的几种方法
  • LangChain【6】之输出解析器:结构化LLM响应的关键工具
  • [ElasticSearch] DSL查询
  • 【Spark征服之路-2.3-Spark运行架构】
  • OpenLayers 分屏对比(地图联动)
  • 【 java 集合知识 第二篇 】
  • CSS 定位:原理 + 场景 + 示例全解析
  • 使用 SymPy 进行向量和矩阵的高级操作
  • uni-app 如何实现选择和上传非图像、视频文件?
  • 山东大学《数据可视化》期末复习宝典
  • 机器学习框架PyTorch
  • Opencv中的copyto函数
  • ESP8266(NodeMcu)+GPS模块+TFT屏幕实现GPS码表
  • 关于双网卡优先级:有效跃点数的解析(设置值×2)
  • day48 python通道注意力
  • 基于AWS Serverless架构:零运维构建自动化SEO内容生成系统
  • Spark 之 DataFrame 开发
  • 内嵌式mqtt server
  • IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
  • halcon 透视矩阵
  • Opencv中的addweighted函数
  • OpenCV 图像色彩空间转换与抠图
  • VTK如何让部分单位不可见
  • uniapp 安卓 APP 后台持续运行(保活)的尝试办法
  • 【Veristand】Veristand环境安装教程-Linux RT / Windows
  • OpenCV为图像添加边框
  • MVC与MVP设计模式对比详解
  • PCB特种工艺应用扩展:厚铜、高频与软硬结合板
  • 加法c++
  • 【驱动】Orin NX恢复备份失败:does not match the current board you‘re flashing onto