快递管理控制台项目
任务描述:
为了熟悉快递管理业务,完成快递管理控制台项目,具体需求如图:
我们将数据存储在集合中,但是在程序被关闭后,存储的数据也就丢失了。那我们就需要用到 IO,使用 IO 技术将快递数据存储到文件中了。文件存储快递信息后,可以在每次启动应用时便读取到文件中的内容,从而实现程序数据的一直存在。 现在我们来开始完成这项项目吧
任务过程
- 明确具体需求
- 选择合适的集合存储快递数据
- 将快递数据存储到文件中
- 程序启动自动加载文件中数据
- 多次测试保证功能齐全,无Bug出现
涉及知识点
1.面向对象
2.类集(集合)
3.IO(文件输入输出)
4.MVC模型
厘清思路
第一步:
先了解快递信息有哪些,我们根据任务描述得知快递信息应该有:
1.快递单号
2.快递公司
3.取件码
4.存储快递位置
根据这些信息,我们来创建一个Express类,用来封装这些信息含有的属性。
代码如下:
在这里插入代码片
public class Express implements Serializable {
private String id; // 快递单号
private String company;// 快递公司
private int code;// 取件码
// 构造方法
public Express() {
super();
}
public Express(String id, String company, int code) {
super();
this.id = id;
this.company = company;
this.code = code;
}
//Setter、Getter
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
//equals、hashCode
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + code;
result = prime * result + ((company == null) ? 0 : company.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Express other = (Express) obj;
if (code != other.code)
return false;
if (company == null) {
if (other.company != null)
return false;
} else if (!company.equals(other.company))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
//toString
@Override
public String toString() {
return "快递单号:" + id + ", 快递公司:" + company + ", 取件码:" + code ;
}
}
怎么样,这里面有接口,有equals和hashCode方法,可能现在我们还看不出它们有什么用处,因为这是我最终写的Express类,它们的用处肯定是有的,我们只有在一步步做的时候才能去会发现它们。好了,接下来我们再看下一步。
第二步:
通过上一步,我们封装了快递信息从而创建出了一个Express类,以后我们无论是添加、删除、修改、查询,直接用Express创建对象,岂不很方便,对吧。现在我们来简单了解一下MVC模型。
M:表示数据的存取
V:表示视图效果
C:表示调用逻辑
我们就这么理解,我们要给用户展示的东西,就是这个V视图效果,我们可以把需要展示的方法功能封装成一个类,里面有我们想要给用户展示看的菜单界面,主页页面,登录界面,操作界面,显示信息界面等等一系列的视图界面或提示用户的方法功能,我们把它封装成一个类。我现在把它封装成了一个View类。到时我们需要用到这些功能方法的话,就直接创建一个View对象,这样的话,代码就可以显得不杂乱。这也是培养我们面向对象的思维能力,我们要慢慢摆脱我们面向过程的思维。从执行者变成一个指挥者吧。
那么M呢,Model 模型,它表示数据信息的存取,我们需要进行添加,删除,修改,查看时,都需要有一个数据信息的存取过程。为了方便,我们同样可以把这些功能方法封装成一个类,到时要用这些功能方法时,我们直接用对象调用就行了。我在这里把它封装成了一个ExpressDao类。
那么C呢,Controller 控制器,程序的入口,负责响应用户操作 并调用相对应的业务逻辑模块 完成整个功能需求。
现在我们对MVC应该有了一个具体的认识了吧,它的作用也有很多,比如:使代码更容易维护,有利于代码复用。
现在我提供一个关于视图效果View类的代码:
在这里插入代码片
public class View {
private Scanner scanner = new Scanner(System.in);
private Random random = new Random();
private List<Integer> li = new ArrayList<Integer>();
//开始
public void start() {
System.out.println("欢迎来到快递柜管理系统");
}
//结束
public void end() {
System.out.println("再见,欢迎下次再来哦...");
}
//接收用户输入
public int input() {
System.out.println("根据相应的序号,请输入:");
int num = 0;
while(true) {
try {
num = scanner.nextInt();
return num;
}catch (Exception e) {
System.out.println("你的输入有误,请重新输入:");
scanner.nextInt();
}
}
}
//打印快递信息
public void printExpress(Express e) {
System.out.println("快递单号:"+e.getId()+",快递公司:"+e.getCompany()+",取件码:"+e.getCode());
}
//输入信息有误
public void inputError() {
System.out.println("你输入的信息有误......");
}
//快递柜空了
public void nullExpress(){
System.out.println("不存在此快递");
}
//随机生成一个取件码
public int code() {
while(true) {
int num = 0;
int code = (int)(random.nextInt(899999)+100000);
for(int i=0; i<li.size();i++) {
if(li.get(i) == code) {
num ++;
}
}
if(num == 0) {
li.add(code);
return code;
}
}
}
//登录
public int login() {
System.out.println("请登录: 1.快递员\t 2.普通用户 \t 3.退出 ");
return input();
}
//管理员
public int aindex() {
System.out.println("请选择你要进行的操作:1.快递录入\t 2.快递删除\t 3.快递修改\t 4.查看所有快递\t5.返回上一级目录");
return input();
}
//用户
public int uindex() {
System.out.println("请选择你要进行的操作:1.取快递\t 2.返回上一级目录");
return input();
}
//快递录入
public Express insert() {
System.out.println("请输入你要添加的快递单号:");
String id = scanner.next();
System.out.println("请输入你要添加的快递公司:");
String company = scanner.next();
int code = code();
Express e = new Express();
e.setId(id);
e.setCompany(company);
e.setCode(code);
return e;
}
//快递已存在
public void expressError() {
System.out.println("此快递已存在...");
}
//快递录入成功
public void InsertSuccess() {
System.out.println("恭喜你,快递录入成功...");
}
//快递录入失败
public void insertError() {
System.out.println("很遗憾,快递录入失败...");
}
//找出要删除的快递
public String delete() {
System.out.println("请输入你要删除的快递单号:");
String id = scanner.next();
return id;
}
//删除确认
public int deleteConfirm() {
System.out.println("确认是否删除: 1.确认\t 2.取消");
return input();
}
//快递删除成功
public void deleteSuccess() {
System.out.println("恭喜你,快递删除成功...");
}
//快递删除失败
public void deleteError() {
System.out.println("很遗憾,快递删除失败...");
}
//找出要修改的快递单号
public String update() {
System.out.println("请输入你要修改的快递单号:");
String id = scanner.next();
return id;
}
//修改成功
public void updateSuccess() {
System.out.println("恭喜你,快递修改成功...");
}
//修改失败
public void updateError() {
System.out.println("很遗憾,快递修改失败...");
}
//查看所有快递
public void printAll(List<Express> list) {
for(Express e : list) {
printExpress(e);
}
}
//取快递
public int getExpress() {
System.out.println("请输入取件码:");
int num = 0;
while(true) {
try {
num = scanner.nextInt();
return num;
}catch (Exception e) {
System.out.println("你的输入有误,请重新输入:");
num = scanner.nextInt();
}
}
}
//取件失败
public void getError() {
System.out.println("很遗憾,取件失败");
}
//取件成功
public void getSuccess() {
System.out.println("恭喜你,取件成功");
}
//取件提醒
public void printGet() {
System.out.println("请您尽快取件");
}
//删除取件码
public void deleteCode(int code) {
li.remove((Integer)code);
}
//快递所在位置
public void address(int index) {
System.out.println("快递在"+index+"号快递柜");
}
}
怎么样,方法看着是不是特多,有点烟花缭乱,为什么要创建那么多方法,甚至只为了输出一句话也要创建一个方法,因为它有利于代码复用啊,有些方法我是要调用多次的,虽然只有一句话,但它能调用到多次,所有为此也是值得的。
第三步:
创建集合,集合有
Set接口的类集
List接口的类集
Map接口的类集
那在这里我选择的是List接口下的ArrayList类集,因为它是我认为比较适合的一个集合,
ArrayList是一个可改变大小的数组.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问,它是异步处理,效率很高,考虑到我这个不需要同步,我就选择它了。
现在我们知道该选择ArrayList类集,我们也知道创建集合是为了存储快递信息,而这些快递信息都封装在Express类,所以我们创建的ArryaList的类型为Express类。创建格式为:
List<Express> list = new ArrayList<Express>();
那我们在哪个类需要用到集合呢?不用想,就是数据信息的存取那个类,也就是我们ExpressDao这个类中,因为这个专门是用来存信息的,用来取信息的,那肯定都是需要在集合里存啊,在集合里取啊,对吧。但是我们得注意把它声明在成员属性里,顺便进行初始化。因为我们都知道在这个类的很多方法功能都需要用到这个集合。否则我们访问的就不是同一个集合。
第四步
现在我们好集合后,但是我们还没有解决问题,因为我们的需求是在程序被关闭后,我们的存储的数据不能丢失,那我们需要用到IO,使用IO技术将集合里的快递数据存储到文件中,文件存储了之后,那我们下次启动时,再从文件里读取快递信息并存储到集合里,这样我们就保证了我们存储的数据不会丢失了。
我的简单思路就是:
在原有的基础上,创建了两个方法
一个是读取文件快递信息的方法,返回值是Express类型的List集合。
在这里插入代码片
//读取快递文件信息
public List<Express> fileInput() throws Exception {
try {
ois = new ObjectInputStream(new FileInputStream(new File("d:/快递.txt")));
Object o = ois.readObject();
list1 = (ArrayList<Express>) o;
ois.close();
return list1;
}catch (EOFException e) {
System.out.println("没有任何快递信息");
}
return null;
}
另一个是存储文件快递信息的方法,把集合里的快递信息写入到文件中去。
在这里插入代码片
//存储快递文件信息
public void fileOutput(List<Express> list) throws IOException {
oos = new ObjectOutputStream(new FileOutputStream(new File("d:/快递.txt")));
oos.writeObject(list);
System.out.println("存储成功...");
oos.close();
}
在这里我用到了对象序列化和反序列化,我在这里简单解释一下这两个知识点。
对象序列化:把对象存储到文件中的过程。
反序列化:把之前序列后的文件中数据再变成对象的过程。
但是请注意:
实现序列化先将把需要序列化的类实现Serializable接口,该接口不需要实现序列化的方法,但是它一定要实现。
实现该接口是为了标注对象是可被序列化的。然后我们创建ObjectOutputStream对象,使用FileOutputStream来构造ObjectOutputStream对象,最后我们使用ObjectOutputStream对象的writeObject(Object o)方法就可以将o对象输出,存储到文件中。
现在知道我们那个Express类为什么要实现那个接口了吧。O(∩_∩)O
然后在进入一些插入、修改、删除、取件等操作时,我们同时也进行一个存储文件信息的操作,这样我们的文件信息就和集合里的快递保持一致了。
我的艰辛路程:
其实在一开始的时候,我对于怎么删除,怎么修改文件里的快递信息真的是想了老半天。我知道怎么读取文件快递信息,怎么存储文件快递信息,但是就是不知道怎么修改或删除文件里的快递信息,我转不过弯了,我给自己挖坑,我很复杂化了,还创建了两个集合,一个是存储文件快递信息的集合,一个是运行在程序刚录入快递信息的集合。最后还是弄不好,出现了很多重复的快递信息。最后求教了别人,才点醒了我。
那么文件这些读取,存储的方法放哪呢,和集合一样,都是放在我们数据存取的类里,也是ExpressDao这个类中。
现在我提供Express类中的代码,:
在这里插入代码片
public class ExpressDao {
private List<Express> list1 = new ArrayList<Express>();
private ObjectOutputStream oos;
private ObjectInputStream ois;
public List<Express> getList1() {
return list1;
}
public void setList1(List<Express> list1) {
this.list1 = list1;
}
//读取快递文件信息
public List<Express> fileInput() throws Exception {
try {
ois = new ObjectInputStream(new FileInputStream(new File("d:/快递.txt")));
Object o = ois.readObject();
list1 = (ArrayList<Express>) o;
ois.close();
return list1;
}catch (EOFException e) {
System.out.println("没有任何快递信息");
}
return null;
}
//存储快递文件信息
public void fileOutput(List<Express> list) throws IOException {
oos = new ObjectOutputStream(new FileOutputStream(new File("d:/快递.txt")));
oos.writeObject(list);
System.out.println("存储成功...");
oos.close();
}
//快递信息的存入
public boolean insert(Express e) {
list1.add(e);//添加快递信息
return true;
}
//快递信息的删除
public boolean delete(Express e) {
list1.remove(e); //删除刚录入的快递信息
return true;
}
//快递信息的修改
public boolean uadate(Express e1, Express e2) {
//修改刚录入的快递信息
list1.remove(e1);
list1.add(e2);
return true;
}
//查看所有快递信息
public List<Express> getAll(){
return list1;
}
//查询单个快递(快递单号查询)
public Express getById(String id) {
//在刚录入的快递信息里找
for(Express e : list1) {
if(e.getId().equals(id)) {
return e;
}
}
return null;
}
//取件码查询
public Express getByCode(int code) {
//在刚录入的快递里取
for(Express e : list1) {
if(e.getCode() == code) {
return e;
}
}
return null;
}
//获取快递的位置
public int getIndex(Express e) {
for(Express ee : list1) {
if(ee.getId().equals(e.getId())&&ee.getCompany().equals(e.getCompany())&&ee.getCode()==e.getCode()) {
return list1.indexOf(ee);
}
}
return 0;
}
}
应该是不难理解的,我有注释的。就涉及了一些我前面所学的面向对象,以及一些Java基础语法,再加上这次所学的IO跟集合,把这些知识点融会贯通一下,我们都可以做出这么一个项目。
第五步:
这一步,我们该思考调用逻辑,结合我们创建的类,以及这些类封装的方法功能,还有我们的任务描述来去调用。
首先
我们需要用到我们创建的View类和ExpressDao类,我们肯定是要调用这两个类里的功能方法的,所以在一开始我们把它声明在成员属性里,创建这两个类的对象,进行初始化。
代码如下:
在这里插入代码片
private static ExpressDao dao;
private static View view;
//对成员属性进行初始化
public static void init() {
dao = new ExpressDao();
view = new View();
}
然后我们根据任务描述以及操作过程来思考。
第一步
我们需要选择我们的身份,我们就创建一个选择身份的方法:
代码如下:
在这里插入代码片
//角色身份权限选择
public static void login() throws Exception {
ha: while(true) {
int num = view.login();
List<Express> li = dao.fileInput();
dao.setList1(li); //读取出来放到集合里去
switch(num) {
//选择管理员
case 1 :
aindex();
break;
//选择用户
case 2:
uindex();
break;
//退出
case 3:
view.end();
break ha;
//输入有误
default:
System.out.println("输入有误...");
break ha;
}
}
}
第二步
我们需要想到操作界面,用户有用户的操作界面,管理员有管理员的操作界面
代码如下:
在这里插入代码片
//管理员操作菜单界面
public static void aindex() throws Exception {
ha: while(true) {
int num = view.aindex();
switch(num) {
//快递录入
case 1:
insert();
//存储文件快递信息
dao.fileOutput(dao.getAll());
break;
//快递删除
case 2:
delete();
dao.fileOutput(dao.getAll());
break;
//快递修改
case 3:
update();
dao.fileOutput(dao.getAll());
break;
//查看所有快递
case 4:
printAll();
break;
//返回上一级目录
case 5:
break ha;
//输入有误
default:
System.out.println("输入有误...");
break ha;
}
}
}
//普通用户操作菜单界面
public static void uindex() throws IOException {
ha: while(true) {
int num = view.uindex();
switch(num) {
//取快递
case 1:
int code = view.getExpress();
Express e = dao.getByCode(code);
if(e!=null) {
view.address(dao.getIndex(e));
view.printExpress(e);
view.printGet();
if(dao.delete(e)) {
view.deleteCode(e.getCode());
}
dao.fileOutput(dao.getAll());
}else {
view.nullExpress();
}
break ;
//返沪上一级目录
case 2:
break ha;
//输入有误
default:
System.out.println("输入有误...");
break ha;
}
}
}
第三步:
管理员的操作方法具体实现的调用
在这里插入代码片
//录入操作
public static void insert(){
Express e = view.insert();
//判断插入的单号是否有重复
if(dao.getById(e.getId())!=null) {
//表示快递已经有了
view.expressError();
}else {
if(dao.insert(e)) {
//表示录入成功,顺便显示录入信息
view.InsertSuccess();
view.printExpress(e);
}else {
//表示录入失败
view.insertError();
}
}
}
//删除操作
public static void delete() {
String id = view.delete();
Express e = dao.getById(id);
if(e!=null) {
int num = view.deleteConfirm();
switch(num) {
//确认删除
case 1:
if(dao.delete(e)) {
view.deleteSuccess();
//删除取件码
view.deleteCode(e.getCode());
}else {
view.deleteError();
}
break;
//取消删除
case 2:
System.out.println("OK,你已取消了删除操作");
break;
default:
System.out.println("你的输入有误...");
break;
}
}else {
//表示没有你要删除的快递
view.nullExpress();
}
}
//修改操作
public static void update() {
String id = view.update();
Express e = dao.getById(id);
if(e!=null) {
view.printExpress(e);
Express ee = view.insert();
if(dao.uadate(e, ee)) {
//修改成功
view.updateSuccess();
view.deleteCode(e.getCode());
}else {
//修改失败
view.updateError();;
}
}else {
//表示没有你要修改的快递
view.nullExpress();
}
}
//查看所有快递
public static void printAll() throws Exception {
List<Express> li = dao.getAll();
if(li.size()>0) {
view.printAll(li);
}else {
System.out.println("快递柜空了...");
}
}
第四步:
结合以上我们创建的方法,思考程序运行的步骤过程。
在这里插入代码片
//主界面
public static void main(String[] args) throws Exception {
init();//初始化方法
view.start();//欢迎用户,提示用户程序已运行
login();//选择身份就可以进行相关操作。因为这个方法已经调用了相关操作的方法。
}
结语
人生或是学习,就像一场马拉松比赛,不到最后一刻,就不要停下,在Java的赛道上,愿你我都能跑在最前列。