提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
经过了一段时间的学习,总算拉了一坨大的(X
本篇文章简单写了点关于图书管理系统的实现
想的跟写的有点区别,有可能上下文衔接做的不太好,也不知道当时写的时候怎么想的,写的风格有可能会变化,请谅解(擦汗
本文代码是一小块一小块,分段来讲的
如有错误,请多指正!
图书管理系统
在敲代码之前,咱先看一下图书馆管理系统,大概是什么东西:
如图所示,咱最先要做的,就是写一个小小的菜单
在这个菜单中我们可以选择我们的身份
1.开始菜单
嗯,这块还是很简单的,用 Scanner 来接收输入值,用 “sout” 进行输出打印
然后选择哪个用户 咱就实例化哪个用户,乱输入就重试(
public static User login() {
Scanner in = new Scanner(System.in);
Time time = new Time();
System.out.println("请输入您的姓名:");
String name = in.nextLine();
System.out.println("尊敬的 " + name + " " + time.getTime() + "好");
System.out.println("请选择您的身份:1.管理员 2.普通用户");
while (true) {
String choice = in.nextLine();
if (choice.equals("1")) {
return new AdminUser();
} else if (choice.equals("2")) {
return new NolmalUser();
} else {
System.out.println("非法输入,请重试!");
}
}
}
这里的 time 是我自己为了好看正的一个 获取时间段并返回问候语的,后面会写
2.用户
由于我们需要有两个用户去进行操作,我们要分开写两个写~
但是这两个之间一定是会有重复的地方吗,所以咱可以利用继承
可以先写一个父类用户,用来给其他两个用户继承并重写
public class User{
public abstract int menu();
}
由于两个用户 都得打印菜单
在两个用户类里重复写两遍菜单 会导致代码冗余(其实就是更麻烦了)
我们可以在父类使用抽象,子类去实现他
这块返回值使用的是 int类 的原因是因为,我们要通过 在这个菜单中 选择的数字(也就是 1,2,3,4,0…)来操作对应的方法(比如 增删查改)
2.1.管理员用户
咱现在创建两个用户类,一个管理员,一个普通用户
让它们继承父类User
,并实现父类的menu()
方法
这样就少了一点重复的代码(挠头
2.1.1.菜单
public int menu() {
Scanner in = new Scanner(System.in);
System.out.println("* 管理员菜单");
System.out.println("1.查找图书");
System.out.println("2.新增图书");
System.out.println("3.删除图书");
System.out.println("4.显示图书");
System.out.println("0.退出系统");
System.out.println("**********");
System.out.println("请输入你的操作:");
}
2.1.2.选择操作
简单来写 可以直接用
Scanner in = new Scanner(System.in);
int choice = in.nextInt();
if(choice >=0 && choice <=4){
return choice;
}else{
System.out.println("输入错误!");
}
但是,这么写的话有个问题
就是,我万一输入的不是数字(int
)的话,就报错啦
所以我们要换一种方法写咯
while (true) {
String choice = in.nextLine();
switch (choice){
case "0":
case "1":
case "2":
case "3":
case "4":
return Integer.parseInt(choice);
default:
System.out.println("非法输入,请重试!");
}
}
由于我学术不精,没学会啥高级的方法,只能用这种笨办法了(甚至这个Integer.parseInt
也是我上网搜的(可以参考下菜鸟教程 Java parseInt() 方法
这里就是我把输入的数字当成字符接收,然后switch
如果不是这几个 “数字” 就是非法输入(这里咱就不怕输入字符导致nextInt
报错了
然后重点来了,返回值这块,简单来说就是给字符转换成数字返回,也就是 int
那么我们User那边的public abstract int menu();
就能接收到 然后进行其他操作咯
还有一件事! 这里之所以用while(true)
是为了让这一整个 选择操作 可以重复运行,总不能输入一次错误 就又得从头 输入用户名-选择身份-选择操作 吧(太麻烦了
2.2.普通用户
大部分和管理员用户的代码差不多~
2.2.1.菜单
public class NolmalUser extends User {
public int menu() {
Scanner in = new Scanner(System.in);
System.out.println("* 普通用户菜单");
System.out.println("1.查找图书");
System.out.println("2.借阅图书");
System.out.println("3.归还图书");
System.out.println("0.退出系统");
System.out.println("**********");
System.out.println("请输入你的操作:");
}
}
2.2.2.选择操作
while (true) {
String choice = in.nextLine();
switch (choice){
case "0":
case "1":
case "2":
case "3":
return Integer.parseInt(choice);
default:
System.out.println("非法输入,请重试!");
}
}
用户暂时先写到这里咯,剩下的要结合后面的来讲,咱接下来写 “书库”
3.书库
图书管理系统,肯定是得有书吧?那么,一本本的书又该存在哪里呢?当然是书库咯!
咱先来讲 这个 “书” 该如何实现
3.1.书
- 咱直接创一个类,就叫… “Book” 吧
- 书是不是得有 书名、作者、价格、类型等等 这种东西吧?(由于我们写的是图书管理系统,会出现借出/归还之类的情况,所以要加一个 “借出状态” 这种东西
- 构造方法也要写上,因为咱要实例化一堆书
- 基本的get set 也要带上,咱一定会用上的,因为 增删之类的操作 一定会涉及到 对书内容的修改(这里只用写能用上的,要不然一堆代码都用不上 浪费了
- 我们还有可能会对 书 进行打印输出(比如显示所有图书),所以咱还得重写一个
toStirng
(直接用自动生成就可以,我这边为了好看,改成了中文
public class Book { //类
String name; //书名
String author; //作者
float price; //价格
String type; //类型
boolean isLend; //是否借出
public Book(String name, String author, float price, String type) {
this.name = name;
this.author = author;
this.price = price;
this.type = type;
}
public String getName() {
return name;
}
public boolean isLend() {
return isLend;
}
public void setLend(boolean lend) {
isLend = lend;
}
@Override
public String toString() {
return "Book{" +
"书名='" + name + '\'' +
", 作者='" + author + '\'' +
", 价格=" + price +
", 类型='" + type + '\'' +
", 是否借出='" + (isLend ? "是" : "否") + '\'' +
'}';
}
}
这样下来,一个 书的类 就差不多完事儿了,后续有什么新的添加进去就好~
3.2.库
现在我们要想一下,怎么样才能把一堆 类 存储在一起?
咱可以使用类数组,数组就是我们的"库"
- 先创建一个书库类 咱就叫它为 BookLIst
- 要把类存在数组里 所以要建一个 类数组
- 书库里默认放四本书(放四大名著吧,别的也可以
- 后续进行操作咱得知道书库里有几本书可以供我们操作,所以要弄一个变量来记录库中书量
- get set 安排上,后面要用到。简单来说想知道书库里某一本书里面是什么,肯定是得先拿出来看看吧(
getBooks
),然后还得放回去吧(setBooks
) - 最后就是,我们需要一个判断用书库是否为满的方法,方便后面判断
public class BookList {
Book[] books = new Book[10];
private int useSize;
public BookList() {
books[0] = new Book("水浒传", "施耐庵", 59.9f, "小说");
books[1] = new Book("三国演义", "罗贯中", 69.9f, "小说");
books[2] = new Book("西游记", "吴承恩", 79.9f, "小说");
books[3] = new Book("红楼梦", "曹雪芹", 89.9f, "小说");
this.useSize = 4;
}
//获取/设置 书
public Book getBooks(int i) {
return books[i];
}
public void setBooks(int i, Book book) {
books[i] = book;
}
//获取/设置 使用量
public int getUseSize() {
return useSize;
}
public void setUseSize(int useSize) {
this.useSize = useSize;
}
//判断是否为满
public boolean isFull(){
return useSize == 10;
}
}
4.各种方法
咱在上面已经把书库写好了,咱现在就要去实现,在用户菜单中的各种方法咯
如图,有几个重复的方法,我们可以复用,所以我们只需要写:
查找、新增、删除、显示、借阅、归还、退出
7个方法就ok啦~
不同的对象 需要同样的功能 这种情况 我们应该用什么呢?
当然是接口咯(目前就学到这个了
public interface IOperations {
public abstract void work(BookList bookList);
}
这里就是 创建了一个接口,里面有个 抽象方法work()
接口 就是抽象方法的集合 (Java 接口 | 菜鸟教程
4.1.查找
竟然要进行查找,难免需要一个查找的目标,咱这边就用 书名 来进行查找啦
咱跟上面一样,先弄清楚我们需要什么,需要怎么做:
- 最重要的就是 我们写的方法 都是在 实现
IOperations
接口(还有接口里的抽象方法),所以我们要用到implements
关键字! - 我们要输入书名,所以需要
Scanner
啦 - 然后就是在书库里进行查找,我们这里就直接用暴力查找,遍历了(其他的有点麻烦
- 查找后只有两种结果,找到了/没找到
整个流程简单来说就是:先拿到书-看书名-确认是否是我要找的书
public class Find implements IOperations {
@Override
public void work(BookList bookList) {
Scanner in = new Scanner(System.in);
System.out.println("请输入要查找的书名:");
String name = in.nextLine();
for (int i = 0; i < bookList.getUseSize(); i++) {
//使用getUseSize()知道目前书库里有几本书,避免超过数组(书库)大小导致内存!
Book book = bookList.getBooks(i);
//从书库中获取当前 i 位置的 书
if(book.getName().equals(name)){
//如果从书里获取的名字 和 我要查找的书名一致 就是找到了,反之没找到
System.out.println("找到了:");
System.out.println(book);
return;
}
}
System.out.println("没找到");
}
}
前两步都很轻松吧,关键在于第三步,我们要如何查找?
跟上面说的一样要用 书名
这时候我们在之前 书库 类里定义的getBooks
方法、书 类里定义的getName
方法派上用场了,知道了书名,咱就可以在遍历的过程中进行对比啦,
书名 是字符串,判断字符串之间内容是否相同 我们可以使用equals()
这样下来查找部分就没有问题咯~
4.2.新增
书库里不能只有四本书吧,所以我们要往里面添加新书
- 判断我的书库 是否有足够的空间 去存储
- 我得知道我要添加的新书的 各种属性(书名、作者、价格、类型)
- 在空位添加一本书
public class Add implements IOperations {
@Override
public void work(BookList bookList) {
if(bookList.isFull()){
System.out.println("书库满了,不能新增!");
return;
}
Scanner in = new Scanner(System.in);
System.out.println("请输入书名:");
String name = in.nextLine();
System.out.println("请输入作者:");
String author = in.nextLine();
System.out.println("请输入价格:");
float price = in.nextFloat();
in.nextLine(); //这里nextFloat()它不会把回车也过消耗掉,所以需要我们手动消耗一下,要不然程序就卡住了
System.out.println("请输入类型:");
String type = in.nextLine();
Book book = new Book(name, author, price, type); //用构造方法创建一本新书
bookList.setBooks(bookList.getUseSize(), book); //放到指定位置(如图
bookList.setUseSize(bookList.getUseSize() + 1); //然后因为添加了一本新书,useSize也要跟着+1
System.out.println("新增图书成功!");
}
}
这段没有什么太复杂的咯
4.3.删除
- 要删除什么书?(遍历查找
- 怎么删?
public class Del implements IOperations {
@Override
public void work(BookList bookList) {
Scanner in = new Scanner(System.in);
System.out.println("请输入要删除的书名:");
String name = in.nextLine();
for (int i = 0; i < bookList.getUseSize(); i++) {
Book book = bookList.getBooks(i);
if (book.getName().equals(name)) {
for (int j = i; j < bookList.getUseSize() - 1; j++) { //如图
bookList.setBooks(j, bookList.getBooks(j + 1));
}
bookList.setUseSize(bookList.getUseSize() - 1);
System.out.println("删除成功");
return;
}
}
System.out.println("查无此书,请重试!");
}
}
如果在for
循环里直接使用i < getSize
的话会出现图里这种情况,也就是内存泄漏
所有过程如图,咱就是进行了一个简单的,复制粘贴(覆盖)+ 删除
4.4.显示
显示的话咱直接上代码!
public class Show implements IOperations {
@Override
public void work(BookList bookList) {
for (int i = 0; i < bookList.getUseSize(); i++) {
Book book = bookList.getBooks(i);
System.out.println(book);
}
}
}
把每个书拿过来 直接sout
就可以了,因为我们在写Book
类的时候就早已把toString
重写过~
4.5.借阅,归还
借阅/归还其实就是 给借出状态更改一下的事儿
直接调用之前在Book
类里定义的setLend
方法就好了
借阅:
public class Lend implements IOperations {
@Override
public void work(BookList bookList) {
Scanner in = new Scanner(System.in);
System.out.println("请输入要借阅的书名:");
String name = in.nextLine();
for (int i = 0; i < bookList.getUseSize(); i++) {
Book book = bookList.getBooks(i);
if ((book.getName().equals(name))) {
if (book.isLend()) {
System.out.println("借阅失败,已被借阅!");
} else {
System.out.println("成功借阅!");
book.setLend(true);
}
return;
}
}
}
}
归还:
public class Return implements IOperations {
@Override
public void work(BookList bookList) {
Scanner in = new Scanner(System.in);
System.out.println("请输入要归还的书名:");
String name = in.nextLine();
for(int i =0;i< bookList.getUseSize();i++){
Book book = bookList.getBooks(i);
if(book.getName().equals(name)){
if(book.isLend()){
book.setLend(false);
System.out.println("成功归还!");
}else {
System.out.println("归还失败,未被借出!");
}
}
return;
}
}
}
看代码就知道 两个方法,本质上没有太大区别,主要是 为了分开这两个功能,重复写了很多代码(暂时没找到解决方法,知识有限)
4.6.退出
啊哈!最喜欢的环节!
嘎嘎简单
public class Exit implements IOperations {
@Override
public void work(BookList bookList) {
System.out.println("正在退出...");
System.exit(0);
}
}
直接一个exit
中断程序就可以啦!!!
4.7.获取时间段
这个方法从网上搜索出来的,我还没学(挠头
出处:java判断时间为上午,中午,下午,晚上,凌晨
public class Time {
Date date = new Date();
SimpleDateFormat df = new SimpleDateFormat("HH");
String str = df.format(date);
int a = Integer.parseInt(str);
public String getTime() {
if (a >= 0 && a <= 6) {
return "凌晨";
}
if (a > 6 && a <= 12) {
return "上午";
}
if (a == 13) {
return "中午";
}
if (a > 13 && a <= 18) {
return "下午";
}
if (a > 18 && a <= 24) {
return "晚上";
}
return str;
}
}
5.结合(用户+方法)
写了这么多方法,咱总算可以结合起来了(抹泪
咱这么多方法,其实只是在实现一个接口,也就是IOperations
接口
那咱怎么用 1,2,3,4,0…去指定执行呢?
跟书库差不多,咱可以把 接口 弄成一个 数组
用下标的方式访问 各种实现接口的方法
User:
public abstract class User {
IOperations[] iOperations; //接口数组
public abstract int menu(); //抽象方法 menu 两个用户重写各自的菜单
public void doIOperations(int choice, BookList bookList) {
this.iOperations[choice].work(bookList);
//谁用 谁就调用 [位置] 的 work方法(在书库里执行)
}
}
AdminUser:
public class AdminUser extends User {
public AdminUser() {
this.iOperations = new IOperations[]{
new Exit(),
new Find(),
new Add(),
new Del(),
new Show()
};
}
...//其他代码
}
这里就是管理员这里的 接口数组里 有这些 实现方法,由于数组下标是从 0 开始
我们第一位 就放 退出
然后就是 查找,增添,删除,查看
通过new
关键字就可以实例化(差不多跟激活一样)这些方法
这里其实就是,只要实例化了,这个用户,他就会顺便实例化跟他对应的方法
NolmalUser:
public class NolmalUser extends User {
public NolmalUser() {
this.iOperations = new IOperations[]{
new Exit(),
new Find(),
new Lend(),
new Return(),
};
}
...//其他代码
}
普通用户这边其实也差不多的
6.main
最关键的地方来咯,也是最麻烦的地方(
咱在上面所有的就是为了现在这个
在上面结合中 我们有一个 执行操作的方法doIOperations
如图所示,咱得靠上面所有代码结合过来看这四行代码(悲
public static void main(String[] args) {
BookList bookList = new BookList();
User user = login();
while (true) {
user.doIOperations(user.menu(), bookList);
}
}
}
- 这里我们先实例化了一个书库
- 调用
login()
方法选择用户(管理/普通)并实例化对象 - 实例化对象同时,实例化了对应用户的
this.iOperations[]
中的方法对象 - 实例化完成后,在打印出来的 选择操作菜单 中进行选择,并以
int
返回给menu()
,也就是
user.doIOperations(user.menu(), bookList)
中的user.menu()
- 这下就可以把书库传入并直接调用
user.doIOperations(user.menu(), bookList)
进行各种操作了
再解释一下这里的图,这是我们在结合过程中写的一段用来操作书库的方法
它的原理就是和图里写的一样:
this | . | iOperations | [ choice ] | . | work | ( bookList) |
---|---|---|---|---|---|---|
管理员用户/普通用户 | 的 | 方法 | 第几个方法 | 中的 | 实现/执行 | 对谁执行(书库) |
总结
写的我汗流浃背,实习期间只有晚上有时间,写了三个晚上(挠头
希望能对大家有点小帮助吧,有些地方含糊不清,表达不出来(求饶
欲知后事如何,请听下回分解…