这两天除了上课时间之外,一直在看一个“客户信息管理系统”,一套代码敲下来,收获颇丰,下面对这个小的项目(或者说是案例,)进行一个总结吧!
具体的解释以及思路,我写在了代码中间,以注释的形式来表现。
要实现的功能
编写一个客户信息管理系统,实现对于客户基本信息的“增、删、查、改”的操作。
虽然看起来功能都不算难,但是编程的过程中,着实不易。
主要将功能封装在4个类中:
CustomerView:
作为主模块,负责菜单的显示和处理用户操作
CustomerList
作为Customer对象的管理模块,内部用数组管理一组Customer对象,并提供相应的添加、修改、删除和遍历方法,
供CustomerViewer调用
Customer
作为实体对象,用来封装客户信息
CMUtility
CMUtility工具类,将不同的功能封装为方法,就是可以直接通过调用方法使用它的功能,而无需考虑具体的功能实现细节
具体的代码实现如下:(另外在文章本文的末尾附有完整的源代码压缩包,可供下载)
1、CustomerView类:
/*
* 作为主模块,负责菜单的显示和处理用户操作
*/
/**
*
* @Description 主模块,负责菜单的显示和处理用户操作
* @author admin
*
*/
public class CustomerView {
/*
* 实例化一个CustomerList类的对象, 要注意的是:在实例化创建CustomerList类的对象customerList的时候,
* 会调用CustomerList类的构造函数,
* 也就是我们自己写上去的那个有参(参数为totalcustomer,即:Customer[]数组长度的)构造函数
*/
private CustomerList customerList = new CustomerList(10);
/*
* 便于测试------>提供一个当前类的构造器, 以便在创建对象的过程中就给对象数组就产生了一个信息
*/
// 构造器
public CustomerView() {
Customer customer = new Customer("王涛", '男', 23, "17513312345", "wt@163.com");
/*
* 添加一个客户,将它放在数组当中
*/
customerList.addCustomer(customer);
}
/*
* 显示《客户信息管理软件》界面的方法
*/
private void enterMainMenu() {
boolean isFlag = true;
while (isFlag) {
System.out.println("\n----------客户信息管理软件----------");
System.out.println(" 1、添加客户");
System.out.println(" 2、修改客户");
System.out.println(" 3、删除客户");
System.out.println(" 4、客户列表");
System.out.println(" 5、退出\n");
System.out.print(" 请选择(1-5):");
char menu = CMUtility.readMenuSelection();
/*
* 得到menu之后,到底是哪一个,需要一个分支语句
*/
switch (menu) {
case '1':
addNewCustomer();
break;
case '2':
modifyCustomer();
break;
case '3':
deleteCustomer();
break;
case '4':
listAllCustomers();
break;
case '5':
/*
* 因为没有写退出的方法,可以直接将退出的方法写到这里
*/
System.out.print("是否确认退出(Y/N):");
/*
* 调用CMUtility.readConfirmSelection() 将输入的字符串(包容误输入)取第一个值,并且转换为对应的大写字母
*/
char isExit = CMUtility.readConfirmSelection();
if (isExit == 'Y') {
isFlag = false;
}
/*
* 如果输入的是N,结束case,就直接回到循环,执行下一次循环了
*/
}
}
}
/*
* 添加客户的操作
*/
private void addNewCustomer() {
System.out.println("---------------------------添加客户-----------------------------");
System.out.print("姓名:");
/*
* 读入姓名
*/
String name = CMUtility.readString(10);
System.out.print("性别:");
char gender = CMUtility.readChar();
System.out.print("年龄:");
int age = CMUtility.readInt();
System.out.print("电话:");
String phone = CMUtility.readString(13);
System.out.print("邮箱:");
String email = CMUtility.readString(30);
/*
* 将上述数据封装到一个对象中,再将这个对象放入customerList[]数组中去
*/
Customer customer = new Customer(name, gender, age, phone, email);
/*
* 不一定可以添加成功
*/
boolean isSuccess = customerList.addCustomer(customer);
if (isSuccess) {
System.out.println("---------------------------添加完成-----------------------------");
} else {
System.out.println("-----------------------客户目录已满,添加失败--------------------------");
}
}
/*
* 修改客户的操作
*/
private void modifyCustomer() {
// System.out.println("修改客户的操作");
System.out.println("---------------------------修改客户-----------------------------");
/*
* 声明一个Customer对象:存储查找到的要修改信息的元素的内容 (是一个地址值或者是一个null) 因为是引用数据类型
*/
int number;
Customer cust;
/*
* 不知道循环要执行多少次 具体的循环次数,依据输入的信息而定,如果输入的一直不合法 就一直在这个循环内,提醒用户输入
*/
for (;;) {
/*
* 这个循环只有两个出口: 1、输入的number==-1,退出修改的功能 2、break,进入到修改信息的具体功能实现的模块
*/
System.out.println("请选择待修改的客户编号(-1退出)");
number = CMUtility.readInt();// 记录要修改的编号
if (number == -1) {
return;// 此时结束此方法,直接回到switch-case中的相应位置
}
/*
* 如果不是-1,也要输入的合理才可以 调用customerList.getCustomer方法 ------>
* 如果合理,就返回对象数组中对应的元素的值(是该元素所存储的对象地址) 如果不合理,根据该函数可知,返回的是null ----------
* 需要注意的是,用户输入的number比实际的索引值要 大1
*/
cust = customerList.getCustomer(number - 1);
/*
* 判断cust是否靠谱
*/
if (cust == null) {
System.out.println("无法找到指定的客户!");
// 此时继续提示用户输入要自改的数据对应的编号----继续这个循环的下一轮
} else {
// 找到相应编号的客户
break;// 将相应的操放到这个for循环的外边写
}
}
/*
* 因为可以跑到这里的,就一定是通过break额 ---->输入的编号值合法的 所以,在这里编写修改相应信息的代码
*/
System.out.println("姓名(" + cust.getName() + "):");
/*
* 如果用户没有直接换行,就以输入的姓名为准 如果用户直接换行,就以cust.getName()得到的值为准
*/
// 记录修改后,各个元素应该有的值
String name = CMUtility.readString(10, cust.getName());
System.out.println("性别(" + cust.getGender() + "):");
char gender = CMUtility.readChar(cust.getGender());
System.out.println("年龄(" + cust.getAge() + "):");
int age = CMUtility.readInt(cust.getAge());
System.out.println("电话(" + cust.getPhone() + "):");
String phone = CMUtility.readString(13, cust.getPhone());
System.out.println("邮箱(" + cust.getEmail() + "):");
String email = CMUtility.readString(30, cust.getEmail());
/*
* 将上述数据封装到一个对象中,再将这个对象放入customerList[]数组中去
*/
Customer newCust = new Customer(name, gender, age, phone, email);
/*
* number-1 是对应的对象数组的索引值
*
* 做替换不一定能够换成,得再加上一个判断
*/
boolean isRepalaced = customerList.replaceCustomer(number - 1, newCust);
if (isRepalaced) {
System.out.println("---------------------------修改完成-----------------------------");
}
// 其实,现编的情况是没有可能会出现的
else {
System.out.println("---------------------------修改失败-----------------------------");
}
}
/*
* 删除客户的操作
*/
private void deleteCustomer() {
System.out.println("---------------------------删除客户-----------------------------");
int number;
/*
* 此循环只有两个出口:
* 1、number==-1,直接接退出删除的功能
* 2、最终的number满足删除的要求,实现了break,进入到剩下的部分
* (此方法剩下的部分的内容是 :实现对于删除的具体操作
* ----实际上也是调用方法来实现的
* 调用的是CustomerList的deleteCustomer方法)
*/
for(;;) {
//具有跟修改相似的循环逻辑
System.out.println("请选择待删除的客户编号(-1退出)");
number = CMUtility.readInt();//注意:number的有效范围是从1 开始到total
if(number == -1 ) {
return;
}
//不是输入-1,确是是要修改信息
/*
* 注意:number-1才是对应的索引值
* 因为用户是从1开始算的
* 而java中是从0开始计算的
*/
Customer customer = customerList.getCustomer(number - 1);
/*
* 可能找不到指定的客户信息----customer不靠谱
* 此时customer的值为null
*/
if(customer == null) {
System.out.println("无法找到指定用户!");
//此时将结束本次循环,进入下一轮的for循环中去
} else {
break;
}
}
//找到了指定的客户,开始执行删除操作
System.out.println("是否确认删除(Y/N)");
char isDelete = CMUtility.readConfirmSelection();
/*
* 判断是否输入Y或者y
* 但是判断的时候只用判断是否为Y即可
* 因为调用的CMUtility.readConfirmSelection(),实现了将输入的字符串中的首字符转换为大写的功能
*/
if(isDelete == 'Y') {
boolean deleteSuccess = customerList.deleteCustomer(number-1);
if(deleteSuccess ) {
System.out.println("---------------------------删除成功-----------------------------");
}
// else {
// System.out.println("---------------------------删除失败-----------------------------");
// }//实际上,没有机会执行
}
//如果输入的是n----直接退出这个方法,回到switch-case,继而回到主界面
// else {
// return;//要不要这个无所谓。因为已经在方法的末尾了
// }
}
/*
* 显示客户列表的操作
*/
private void listAllCustomers() {
System.out.println("---------------------------客户列表-----------------------------");
/*
* 先判断数组(是对象数组)中存放的有没有数据
*/
int total = customerList.getTotal();
if (total == 0) {
System.out.println("没有客户记录!");
} else {
/*
* 找到客户记录
*/
System.out.println("编号\t姓名\t性别\t年龄\t电话\t\t邮箱");
/*
* 列出客户记录
*/
// 获取到数组中存储到的数据
Customer[] custs = customerList.getAllCustomers();
for (int i = 0; i < custs.length; i++) {
/*
* 新造的数组中的每个元素,存储的都是对应的对象的存储地址 也就是一个对象元素
*/
Customer cust = custs[i];
System.out.println(i + 1 + "\t" + cust.getName() + "\t" + cust.getGender() + "\t" + cust.getAge() + "\t"
+ cust.getPhone() + "\t" + cust.getEmail());
}
}
System.out.println("-------------------------客户列表完成----------------------------");
}
public static void main(String[] args) {
/*
* 登陆进去要先显示主菜单 main()方法想要调用显示主菜单的方法 就应该先创建一个当前类的对象
*/
CustomerView view = new CustomerView();
// 显示主菜单
view.enterMainMenu();
}
}
2、CustomerList类
/*
* 作为Customer对象的管理模块,
* 内部用数组管理一组Customer对象,
* 并提供相应的添加、修改、删除和遍历方法,
* 供CustomerViewer调用
*/
/**
*
* @Description 作为Customer对象的管理模块,内部用数组管理一组Customer对象,
* @author admin
*
*/
public class CustomerList {
//属性
private Customer[] customers ;//用来保存客户对象的数组
private int total = 0;//记录已保存客户对象的数量
/*
* total的值是0 ,
* 显示初始化为0,或者不写的话,默认的初始化值也是0
*/
//提供以下构造器、方法
/*
* 构造器
* 初始化数组,
* 参数totalcustomer表示数组有多长
*/
public CustomerList(int totalcustomer) {
/*
* 当创建CustomerList对象的时候,就在这个构造函数中,将数组初始化
* 操作数组之前一定要将其初始化
*/
customers = new Customer[totalcustomer];
}
/*
* 增----将指定的客户customer添加到数组中
* 添加成功----返回true
* 添加失败----返回false(满了就失败了)
*/
public boolean addCustomer(Customer customer) {
/*
* 先判断是否满了----满了就添加不进去了
*/
if(total >= customers.length) {
return false;//已经满了
}
/*
* 没有满的话,直接添加
* ----添加的话,先放到数组的索引所指的位置,再将索引值 +1
*/
// customers[total] = customer;
// total++;
//或者
customers[total++] = customer;
return true;
}
/*
* 修改指定位置上的客户信息
* ----形参是两个值,一个是要修改的下标索引;另一个是数组对应的元素要改的值
*/
public boolean replaceCustomer(int index,Customer cust) {
/*
* index决定了是否可以修改
*/
if(index < 0 ||index >= total) {
return false;
}
customers[index] = cust;
/*
* 这里改的是指针
* 原因在于:通过构造器创造出来的数组,其数组元素是Customer,
* 而Customer又是一个对象,
* ---->所以又new出来Customer对象,每个对象的地址值给了存储它的数组元素
* -------->数组中的每个位置放置的是:一个Customer类型的对象的地址
* -----------------
* 现在做的replace操作,就是:
* 又new出来一个对象,它的地址为cust
* 现在需要将数组中索引值为index的元素中存储的值,改为cust
* (使index的索引,指向地址cust存储的Customer对象)
* 而原来的index处存储的那个地址所指向的Customer对象,因为没有指针指向,就被当做垃圾销毁
*/
return true;
}
/*
* 删除指定索引位置上的客户
*/
public boolean deleteCustomer(int index) {
/*
* index决定了是否可以删除,跟修改一样,存在了才可以删除
*/
if(index < 0 ||index >= total) {
return false;
}
/*
* 删掉的如果是位于中间位置的索引的话,不能直接将该索引对应的值置为null
* 应将要删掉的索引的,后边的元素往前移,将现有的最后一个存储数据的索引值置为null
* ------>这是因为数组是一段连续的空间,不允许中间空着,后边放别的
*/
/*
* 写一个循环,将index小后边的元素一个个往前移
* ---->需要注意的是:total记录的是元素的个数,
* 即:比最后一个存储了元素的索引 大1
* 但是需要注意的是,因为i表示的是将i+1的元素放到i上,所以i的最大值为total-2
* 如果在这里不注意的话,容易产生下标越界的情况
*/
for(int i = index;i < total - 1;i++) {
customers[i] = customers[i + 1];//数组元素前移
}
//最后有数据的元素需要置null
// customers[total - 1] = null;
// total--;//当前存储元素的索引最大值,也应该作出相应的变化
//或者合并:
customers[--total] = null;
return true;
}
/*
* 查----获取所有的客户信息
* 返回的是Customer[]类型的数组的首地址
*/
public Customer[] getAllCustomers() {
/*
* 因为已有的数组中可能有空的元素
* 所以不能直接返回已有的数组
* ---->应该重新造一个元素来存储
*/
Customer[] custs = new Customer[total];
//数组的复制
for(int i = 0;i < custs.length;i++) {
/*
* 并不是把每一个Customer对象都复制一份,复制的只是Customer[]数组中存储的地址值
*/
custs[i] = customers[i];
}
return custs;
}
/*
* 查----查循一个,具体查哪一个的话,按照索引判断
* 获取指定位置上的客户
* 返回值的类型是:Customer类的一个对象,
* ----也即:Customer[]数值中的一个元素(虽然返回的是一个地址值,但是这个地址值指向的是一个Customer类的对象)
* 又因为引用类型的默认初始化值为null,所以如果找不到的话,返回一个null即可
*/
public Customer getCustomer(int index) {
/*
* index这个索引值,不一定靠谱,因为这个位置上不一定存储有元素
*/
if(index < 0 || index >= total) {
return null;//找不到
}
return customers[index];
}
/*
* 获取已经存储的客户的数量
*/
public int getTotal() {
/*
* 因为total的值,代表的是所有的客户总数
* 也是下一个将要存储的数组元素的索引值
*/
return total;
}
}
3、Customer类
/*
* 作为实体对象,用来封装客户信息
*/
/**
*
* @Description
* @author admin
*
*/
public class Customer {
private String name;//客户姓名
private char gender;//性别
private int age;//年龄
private String phone;//电话号码
private String email;//电子邮箱
//getxx、setxx方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
//提供构造器
//先写一个空参的
public Customer() {
}
//再写一个全参的
public Customer(String name, char gender, int age, String phone, String email) {
this.name = name;
this.gender = gender;
this.age = age;
this.phone = phone;
this.email = email;
}
}
//实质上就是一个Javabean
4、CMUtility类
/*
* defaultValue
* ----如果用户不输入字符而直接回车,方法将以defaultValue作为返回值
* 在修改客户时调用
*/
import java.util.*;
/*
* CMUtility工具类,
* 将不同的功能封装为方法,
* 就是可以直接通过调用方法使用它的功能,而无需考虑具体的功能实现细节
*/
public class CMUtility {
private static Scanner scanner = new Scanner(System.in);
/*
* 用于界面菜单的选择
* 该方法读取键盘:
* 如果用户键入‘1’-‘5’中的任意字符,则方法返回。
* 返回值为用户所选择的功能对应的字符
*/
public static char readMenuSelection() {
char c;
/*
* 设置循环的结束条件是:
* 读取到有意义的字符
*/
for(;;) {
String str = readKeyBoard(1,false);
c = str.charAt(0);
if(c != '1' && c != '2' &&
c != '3' && c != '4' && c != '5') {
System.out.print("选择错误,请重新输入:");
}else break;
}
return c;
}
/*
* 从键盘读取一个字符,并将其作为方法的返回值
* 这里读取的是性别
* ----
* 与下边修改性别的方法构成重载
*/
public static char readChar() {
String str = readKeyBoard(1,false);
return str.charAt(0);
}
/*
* 从键盘读取一个字符,并将其作为方法的返回值
* 如果用户不输入字符而直接回车,方法将以defaultValue 作为返回值
* 修改性别的时候用到
* ------
* 如果用户没有输入的话,就拿形参的值再去填充,把它作为默认的值去返回
*/
public static char readChar(char defaultValue) {
String str = readKeyBoard(1,true);
return (str.length() == 0 )? defaultValue:str.charAt(0);
}
/*
* 从键盘读取一个长度不超过2位的整数
* 读取的是年龄
*
*/
public static int readInt() {
int n;
for(;;) {
String str = readKeyBoard(2,false);
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.println("数字输入错误,请重新输入:");
}
}
return n;
}
/*
* 从键盘读取一个长度不超过2位的整数,并将其作为方法 的返回值
* 如果用户不输入字符而直接回车,方法将以defaultValue 作为返回值
* 在修改年龄的啥时候用到
*
*/
public static int readInt(int defaultValue) {
int n;
for(;;) {
String str = readKeyBoard(2,true);
if(str.equals("")) {
return defaultValue;
}
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.println("数字输入错误,请重新输入:");
}
}
return n;
}
/*
* 从键盘读取一个长度不超过limit的字符串,并将其作为方法的返回值
* 读取的是电话号码
*/
public static String readString(int limit) {
return readKeyBoard(limit,false);
}
/*
* 从键盘读取一个长度不超过limit的字符串,将其作为方法的返回值
* 如果用户不输入字符而直接回车,方法将以defaultValue 作为返回值
* 修改的时候用到的方法
*
*/
public static String readString(int limit,String defaultValue) {
String str = readKeyBoard(limit,true);
return str.equals("")? defaultValue : str;
}
/*
* 用于确认选择的输入。该方法从键盘读取‘Y’或‘N',并将其作为方法的返回值
* ----用于删除和退出的时候的 再次确认
*
*/
public static char readConfirmSelection() {
/*
* 存储从键盘读入的字符串的第一个值(y 或者 n,或者错误的输入),
* 并在确定输入的是y 或者 n,的情况下,返回对应的大写字母,以便进行比对
*/
char c;
/*
* 循环的结束条件为空
* 真正判断----结束循环的条件,在循环体中,
* 何时结束循环,由输入的情况而定
*/
for(;;) {
/*
* 将从键盘读取到的字符串,转换成大写再进行判断
* 这样不论输入‘Y’还是‘y’,都可以使得正确退出
*/
String str = readKeyBoard(1,false).toUpperCase();
/*
* 读取的是str中的第一个字符
* 这样可以排除用户输入多余字符,而不能使程序正常结束的情况
*/
c = str.charAt(0);
if(c == 'Y' || c == 'N') {
break;
} else {
System.out.println("选择错误,请重新输入:");
}
}
return c;
}
private static String readKeyBoard(int limit,boolean blankReturn) {
String line = "";
while(scanner.hasNextLine()) {
/*
* 从键盘读取的字符串,赋值给line
*/
line = scanner.nextLine();
if(line.length() == 0) {
/*
* 如果line(从键盘读取到的字符串的长度为0----没有读入字符串)
*/
if(blankReturn) {
return line;
} else {
continue;
}
}
if(line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不大于"+limit+")错误,请重新输入: ");
continue;
}
break;
}
return line;
}
}
源代码可以在这里下载: