利用文本文件重构WoniuATM文章摘要
前面的预备知识我们花了很大篇幅给大家介绍Java中如何实现对数据的持久化管理,而不是停留在内存层面。总结下来基本上就是文件型持久化,或者数据库型持久化。所以本次实验,我们将利用文本文件的持久化操作来完成对WoniuATM的重构,使之更好地应用起来。
1.掌握文本文件持久化的具体应用,解决实际问题。
2.加深对文本文件处理的理解,并在后续学习中继续应用。
1.创建一个文本文件内容
在文本文件中,用每一行作为一条用户信息,主要保存用户名,密码和余额即可。同时用逗号来分隔每一个字段值(即CSV逗号分隔符文件),便于后续解析和处理该文件时以逗号作为标志。在当前项目根目录下新建一个目录“data”,并在该目录中新建一个文件文件“userdata.txt”,内容如下:
woniuxy,123456,1000.0 admin,654321,2000.0 qiang,666666,3000.0 zhangsan,111111,4000.0 |
2.设计一个公共类处理文本文件
首先,我们可以定义一个公共处理类:“FileHandler”,该类主要用于处理文本文件的读写操作,可规划如下功能:
(1)按行读取该文本文件内容,将每一行保存在一个List对象中并返回,用于简化读取文件的操作。因为一条用户信息就是一行,所以无论是用于后续读取详细的用户,密码,余额等信息还是写入这些信息,都能够比较方便地进行操作和重用。
(2)按用户名查询到某一行的用户信息,并将该用户信息保存到一个字符串数组中并返回,用于快速对用户信息进行查询。
(3)将文本内容写入到文本文件中,并通过参数传递来决定是覆盖写入还是将内容附加到后面。
(4)由于在转账或存取款过程中牵涉到余额的修改,所以此处牵涉到内容的更新,但是由于我们无法针对一个文件文件直接更新某一条用户信息的某一个字段,所以最好的处理方式是整体更新。
最终,根据上述功能要求,我们设计如下四个方法来完成功能处理:
package com.woniuxy.atm.three; import java.util.List; public class FileHandler { // 从userdata.txt文件中读取内容,并将每一行内容添加到一个List对象中 public List<String> readDataFromFile() { } // 从一个List对象中,根据用户名解析出来一条用户信息,保存在数组中并返回 public String[] getUserInfo(String username) { } // 往userdata.txt文件写入内容,并返回写入的成功状态 public boolean writeDataToFile(String content, boolean isAppend) { } // 当进行转账时,产生对整个文本文件的更新内容,用以修改双方账户的余额 public String generateUpdateData(String username, double money) { } } |
3.实现按行读取的方法“readDataFromFile”
public List<String> readDataFromFile() { // 定义一个List对象,用于代替数组的使用 List<String> contentList = new ArrayList<String>(); // 获取文件的路径 String userDir = System.getProperty("user.dir"); File file = new File(userDir + "\\data\\userdata.txt"); try { InputStream in = new FileInputStream(file); InputStreamReader reader = new InputStreamReader(in, "UTF-8"); BufferedReader br = new BufferedReader(reader); String line = ""; while ((line = br.readLine()) != null) { contentList.add(line); } } catch(Exception e) { e.printStackTrace(); } return contentList; } |
4.实现按用户名获取信息的方法“getUserInfo”
将用户名作为参数传递,并根据逗号分隔该用户名对应的某行内容的三列信息,最后将这三列信息保存到一个字符串数组中返回即可:
public String[] getUserInfo(String username) { List<String> contentList = this.readDataFromFile(); String[] userArray = new String[3]; for (int i=0; i<contentList.size(); i++) { // 利用字符串的split方法,可以将一行内容按分隔符分隔为一个数组 if (contentList.get(i).split(",")[0].equals(username)) { // 数组的第1个元素为用户名,第2个为密码,第3个为余额 userArray[0] = contentList.get(i).split(",")[0]; userArray[1] = contentList.get(i).split(",")[1]; userArray[2] = contentList.get(i).split(",")[2]; } } return userArray; } |
5.实现写入文件内容的方法“writeDataToFile”
该操作没有什么特别之处,只是将从参数传递进来的内容如实写入到文件中而已。
public boolean writeDataToFile(String content, boolean isAppend) { boolean isWriteOk = false; // 写入文件是否成功的标志 String userDir = System.getProperty("user.dir"); String fileName = userDir + "\\data\\userdata.txt"; File file = new File(fileName); try { OutputStream in = new FileOutputStream(file, isAppend); OutputStreamWriter writer = new OutputStreamWriter(in, "UTF-8"); BufferedWriter bw = new BufferedWriter(writer); bw.write(content, 0, content.length()); bw.flush(); bw.close(); writer.close(); in.close(); isWriteOk = true; } catch (Exception e) { isWriteOk = false; System.out.println("写入注册信息到文件时,出错." + e.getMessage()); } return isWriteOk; } |
6.实现产生更新内容的方法“generateUpdateData”
由于我们无法利用Java代码直接修改一个文件中的特定某一行内容,因为Java的文件处理方法只提供了读取和写入两在类型的接口。所以要实现对一个文本文件的更新操作,只能是将其文本内容全部读取出来,再对相应的行的内容进行修改,修改后再整体写入到文件。具体的实现代码如下:
public String generateUpdateData(String username, double money) { // 先获取到当前的用户信息,及 List<String> contentList = this.readDataFromFile(); String[] userInfo = this.getUserInfo(username); // 如果是转账给别人或取款,则money为负数,如果是存款或收款,money为正数 double balance = Double.parseDouble(userInfo[2]) + money; // 根据用户名搜索到对应的某一行用户信息,将其余额进行修改后更新至contentList中 for (int i=0; i<contentList.size(); i++) { if (contentList.get(i).split(",")[0].equals(username)) { String password = contentList.get(i).split(",")[1]; contentList.set(i, username + "," + password + "," + balance); } } // 将整个更新过后的contentList对象中的内容赋值给字符串newContent String newContent = ""; for (int i=0; i<contentList.size(); i++) { newContent += contentList.get(i) + "\r\n"; } return newContent; } |
7.定义UserManager和AccountManager类
我们已经在前面的实验中利用数据类实现(失血模型)实现了一个基于内存的WoniuATM的核心功能,对于UserManager和AccountManager类的功能也有了充分的理解。那么,在进行持久化的重构过程中,我们基本上保持着这两个类相同的结构,相同的方法名称,只是代码实现不一样而已,所以此处不再展示代码结构。
8.检查用户名是否存在
// 定义类成员变量handler,用于整个UserManager类中其它方法的调用 FileHandler handler = new FileHandler(); // 检查用户名是否存在: >=0: 存在, -1: 不存在 public int checkUserExist(String username) { List<String> contentList = this.handler.readDataFromFile(); int userIndex = -1; for (int i=0; i<contentList.size(); i++) { if (contentList.get(i).split(",")[0].equals(username)) { userIndex = i; break; } } return userIndex; } |
9.完成用户的注册功能
public void register() { Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username = sc.next(); if (this.checkUserExist(username) != -1) { System.out.println("你注册的用户名已经存在,请重新输入."); this.register(); } else { System.out.println("请输入密码:"); String password = sc.next(); System.out.println("你输入你的余额:"); double balance = sc.nextDouble(); String content = username + "," + password + "," + balance + "\r\n"; this.handler.writeDataToFile(content, true); this.ui.mainMenu(); } } |
10.完成用户的登录功能
// 为了后续操作方便,为UserManager定义一个静态变量,将当前登录成功的用户名记录起来 public static String loggedUsername = ""; public void login() { // 使用String的方法split(",")来提取每一行的内容 Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username = sc.next(); System.out.println("请输入密码:"); String password = sc.next(); if (this.doLogin(username, password)) { System.out.println("恭喜你,登录成功."); UserManager.loggedUsername = username; this.ui.subMenu(); } else { System.out.println("抱歉,登录失败."); this.login(); } } private boolean doLogin(String username, String password) { List<String> contentList = this.handler.readDataFromFile(); boolean isLoginOk = false; int userIndex = this.checkUserExist(username); if (userIndex != -1) { String line = contentList.get(userIndex); if (line.split(",")[1].equals(password)) { isLoginOk = true; } } return isLoginOk; } |
11.完成用户的转账功能
public void transfer() { // 向某一个已经存在的用户转一笔钱,自己的账户钱会减少,对方账户钱会增加 Scanner sc = new Scanner(System.in); System.out.println("请输入对方账户:"); String toAccount = sc.next(); if (um.checkUserExist(toAccount) != -1) { System.out.println("请输入转账金额:"); double money = sc.nextDouble(); // 为当前登录的转出帐户设置余额减少并写入文本文件中 String fromAccount = UserManager.loggedUsername; String fromContent = this.handler.generateUpdateData( fromAccount, -money); this.handler.writeDataToFile(fromContent, false); // 为目标账户设置余额增加并写入文本文件中 String toContent = this.handler.generateUpdateData(toAccount, money); this.handler.writeDataToFile(toContent, false); System.out.println("恭喜你,转账成功."); this.ui.subMenu(); } else { System.out.println("你输入的目标账户不存在."); this.transfer(); } } |
1.在转账过程中,我们并没有对其转账金额的有效性进行检验,请完善此功能。
2.请继续利用文本文件处理方法完成用户的存取款,查询余额等功能。
3.请利用文本文件管理用户的手机号码,并实现修改密码,修改手机号码等功能。
4.请实现利用手机号码加密码的方式实现用户登录和转账等功能。
5.请为AccountManager类添加操作流水记录功能,并保存起来。
版权所有,转载本站文章请注明出处:蜗牛学院在线课堂, http://www.woniuxy.com/note/194