04
08

实现WoniuATM的注册与登录

基于前面所学知识,我们已经可以尝试完成WoniuATM模拟系统的基础功能了。那么本实验就重点来完成WoniuATM模拟系统的操作菜单绘制(基于命令行),以及注册和登录两个核心功能,并完成简单的函数(方法)调用。


试验简介



基于前面所学知识,我们已经可以尝试完成WoniuATM模拟系统的基础功能了。那么本实验就重点来完成WoniuATM模拟系统的操作菜单绘制(基于命令行),以及注册和登录两个核心功能,并完成简单的函数(方法)调用。




试验目的


1.掌握命令行菜单的绘制方法和调用方式。

2.利用数组完成用户的注册和登录。

3.对目前系统存在的问题有清楚的认识,并通过后续学习继续完善。




试验流程



1.设计程序的基本结构。


在程序设计中,我们不可能把所有的代码都放在main主方法中,这样可以想像当代码量多了以后是几乎不可能行得通的。不单无法维护,代码也无法重用,同时更不能对代码结构进行各种优化。所以,无论哪门程序设计语言,无论是面向对象还是面向过程的编程语言,都支持利用类或者函数(或方法)来针对不同的功能利用不同的函数(或方法)来处理,这样可以从根本上避免程序的结构性混乱。

 

所以,在完成一个程序设计之前,通常我们可以简单地先划分出程序要实现的小功能,并将每个小功能作为一个函数(或方法)来处理。在Java面向对象程序中,我们通常称之为方法。我们可以将本实验的功能细分为四个小功能:主菜单,子菜单,注册,登录,所以我们可以先规划如下代码结构:


package com.woniuxy.atm.one;

 

public class MainUI {

public static void main(String[] args) {

MainUI ui = new MainUI();   // 定义当前类的变量(实例)ui

ui.mainMenu();       // 调用当前类的方法mainMenu()

}

// 绘制主操作菜单(登录前)

public void mainMenu() {

}

// 绘制子操作菜单(登录后)

public void subMenu() {

}

// 注册功能

public void register() {

}

//登录功能

public void login() {

}

}


2.实现登录前的主菜单。


基于命令行的操作,其实主要通过输出一个菜单提示,然后根据用户的输入值进行对比和判断,利用分支结构去调用不同的功能即可实现,所以主菜单的实现功能代码如下:


// 绘制主操作菜单(登录前)

public void mainMenu() {

System.out.println("=====欢迎使用蜗牛ATM无限制存取款系统=======");

System.out.println("===请输入你的选项,1:登录   2:注册   3:退出 ===");

System.out.println("==================================");

Scanner sc = new Scanner(System.in);

String option = sc.next();

switch (option) {

case "1":

this.login();     // this: 代表本类的实例(只适用于非静态方法之间的调用)

break;

case "2":

this.register();

break;

case "3":

System.exit(0);   // 退出当前程序

break;

default:

System.out.println("你输入的选项错误,请重新输入。 ");

this.mainMenu();    // 当输入错误的时候,通过递归调用自己,重新提示输入

break;

}

}


3.实现登录成功后的子菜单。


// 绘制子操作菜单(登录后)

public void subMenu() {

System.out.println("====================请输入你的选项:===============");

System.out.println("==1:查询余额   2:转账   3:取款   4:存款   5:返回主菜单   

6:退出==");

System.out.println("==============================================");

Scanner sc = new Scanner(System.in);

String option = sc.next();

switch (option) {

case "1":

// 调用查询余额的方法

break;

case "2":

// 调用转账的方法

break;

case "3":

// 调用取款的方法

break;

case "4":

// 调用存款的方法

break;

case "5":

this.mainMenu();  // 返回主菜单

break;

case "6":

System.exit(0);   // 退出

break;

default:

System.out.println("你输入的选项错误,请重新输入。 ");

this.subMenu();    // 当输入错误的时候,通过递归调用自己,重新提示输入

break;

}

}


4.利用数组实现注册功能。


要实现用户的注册功能,我们必须首先定义一个存储方式,用于保存用户注册的账户信息,进而在登录验证或账户处理过程中能够记录相应的操作。从目前我们所掌握的知识来说,只有数组是可以用来保存多组信息的,普通的变量无法完成这一目的。所以我们优先考虑使用数组来完成。


但是数组通常是用来保存一类数据类型的,我们要实现注册功能,或者说一个账户信息至少应该包含三个信息:“用户名,密码,余额”,这还不包括其它账户信息,比如联系方式,真实姓名等等。我们不妨先简化该模型,简化为就三个关键信息:“用户名,密码,余额”。那么现在的问题关键点就在于,如果利用数组来更好地完成这三个信息的处理,进行多账户管理呢?

 

我们可以有三种方式来完成:


(1) 分别为三个关键信息定义三个数组,保持数组下标和数组长度的充分一致性,这样每个下标对应的信息就是同一个账户的信息。

(2) 定义一个二维数组,数组当中的每一维对应一个账户信息,里面包含三个关键信息。

(3) 使用面向对象的失血模型定义一个数据类,通过该类的属性来保存其信息,每一个账户对应一个数据类实例,通过数组或列表进行管理。(此方法需要完成面向对象的知识学习后方可理解)


但是无论以哪种方式来实现,我们可以看到,数组在其中都扮演着重要的作用。同时,由于在Java中数组的长度是固定的,不可变的,所以我们要么使用先预设固定长度的方式将就用,要么采用数组动态扩展的方式均可,这一部分知识,前面我们已经做过讲解。

 

下面我们来看看利用三个数组来完成用户注册,并为该账户设置初始余额5000元的实现代码:


// 注册功能

public void register() {

// 设置数组的初始长度为10,表示可注册10个账户

String[] users = new String[10];

String[] passes = new String[10];

int[] balance = new int[10];


Scanner sc = new Scanner(System.in);

System.out.println("请输入你的用户名:");

String username = sc.next();


System.out.println("请输入你的密码:");

String password = sc.next();


users[0] = username;

passes[0] = password;

balance[0] = 5000;


System.out.println("恭喜你,注册开户成功,请选择操作: 1.登录  2.注册  3.返回.");

String option = sc.next();

switch(option) {

case "1":

this.login();

break;

case "2":

this.register();

break;

case "3":

this.mainMenu();

break;

default:

this.mainMenu();

break;

}

}

 

我们来分析一下,上述代码存在哪些问题:


(1) 由于我们将三个数组定义在方法体register()内部,所以每一次调用该方法,数组都会被重新初始化,所以我们的注册功能虽然看上去逻辑上没有问题,但是事实上是无效的。


(2) 由于我们每次注册都在给三个数组的下标【0】赋值,那么相当于我们从来都是在注册一个账户而已,显然这也是没有实际意义的。


(3) 注册账户本身不是目的,账户注册后我们需要登录,需要完成存取款等操作,这才是有价值的功能。但是如果我们把这三个数组定义在方法体内部(称为局部变量),在其它方法体里面是无法读取到其内容的。那么,登录如何获取和验证呢?我们不可能在登录方法体login()内部又重新定义三个数组,否则注册的账户信息跟登录的账户信息根本就不是同一处来源,没有意义。


(4) 注册方法体内还缺乏一个基本的校验,那就是如果要注册的账户信息已经存在,那么就不应该继续接受注册,而应该提醒用户该账户信息已经存在,不允许重复注册。


那么,要如何解决这些问题呢?


5.实现登录功能验证。


我们假定账户信息已经保存在数组中,那么如何实现登录的验证呢?核心代码如下:


public void login() {

// 此处的定义只是为了展示代码逻辑,无法真正实现登录

String[] users = new String[10];

String[] passes = new String[10];


Scanner sc = new Scanner(System.in);

System.out.println("请输入你的用户名:");

String username = sc.next();


boolean isUserOk = false;

int index = -1;

for (int i=0; i<users.length; i++) {

if (username.equals(users[i])) {

// 一旦用户名存在,则退出循环

isUserOk = true;

index = i;

break;

}

}


if (isUserOk == false) {

System.out.println("你输入的用户名不正确,请重新输入.");

this.login();

}


System.out.println("请输入你的密码:");

String password = sc.next();


if (password.equals(passes[index])) {

System.out.println("恭喜你,登录成功,现在进入操作菜单。");

this.subMenu();

}

else {

System.out.println("请输入的密码不正确,请重新输入.");

this.login();

}

}

 

当然,同样的问题,注册的账户信息和登录的账户信息必须保存在同一个数组中。这一问题请读者朋友与上一个问题一起解决即可。


6.利用二维数组实现注册。


public void register2() {

String[][] accounts = new String[10][3];


Scanner sc = new Scanner(System.in);

System.out.println("请输入你的用户名:");

String username = sc.next();


System.out.println("请输入你的密码:");

String password = sc.next();


accounts[0][0] = username;

accounts[0][1] = password;

accounts[0][2] = "5000";// 由于数组类型为字符串型,所以余额也需要加上双引号


System.out.println("恭喜你,注册开户成功,请选择操作: 1.登录  2.注册  3.返回.");

String option = sc.next();

switch(option) {

case "1":

this.login();

break;

case "2":

this.register();

break;

case "3":

this.mainMenu();

break;

default:

this.mainMenu();

break;

}

}

 

通过上述代码,我们也可以看到,由于数组是字符串类型,所以本来是整数的余额也必须强制加上双引号以保持类型的匹配,这显然又产生了一个新的问题。同时,对于登录验证来说,其操作方式类似。但是之前存在的问题依然存在,我们必须要通过优化代码,解决上述问题,进而才可能继续往下实现代码功能。





思考练习



1.请利用数组完善用户的注册与登录功能。

2.请尝试完成用户的查询全额和转账的功能。

3.请总结上述代码的实现存在哪些问题。







为了答谢大家对蜗牛学院的支持,蜗牛学院将会定期对大家免费发放干货,敬请关注蜗牛学院的官方微信。


20181009_153045_341.jpg




版权所有,转载本站文章请注明出处:蜗牛学苑, https://www.woniuxy.cn/article/134