说明

这是一个完善了但又不完善的笔记,或许以后会更新

可以参考但请务必超越

源文件


Tools


Typora
PicGo

图书管理系统

前面较难的面向对象确实是很难消化

所以来写一个图书管理系统加深理解

这篇文章只会简单的写一个小系统,太复杂的当然没有...

说不定以后会更新扩展

涉及到的知识点:

包,类,对象,抽象类,接口,封装,继承,多态,顺序表

可以说一个小练习就全部练习到位

注意

  • 图书馆的管理系统,因为现实中肯定是有业务员管理员在的,所以对于业务和系统功能的判定,每个人想法可能都不一样

在工作中自然有产品经理去决定

这篇文章因为只是要拿来练习,所以业务上的处理不是重点

  • 因为要面向对象,所以程序所要实现的功能,也就是类、接口什么的,都会分开写成单独的文件,当然全部都写到表里也是可以的

1.创建包

首先,我们要写的管理系统肯定是要有书和用户吧

他们在程序里都是数据

那我们就创建两个包来存放

一个是书(book),一个是用户(user)

当然后面还要实现功能,或许还会创建operation等包

2.书

图书馆嘛

核心必然是书

那我们就先把一本书所具备的数据写出来,到后面才可以创建新书,实现功能

2.1 书的数据

图书馆存的书,首先把要存入的数据写出来

种类价格书名作者,当然因为要外借,所以还要有一个是否借出的布尔类型数据

当然是写成一个public类,数据是private封装起来。别忘了构造方法

public class Book {
    private String name;//书名
    private String author;//作者
    private int price;//价格
    private String type;//类型
    private boolean isBorrowed;//是否借出

    public Book(String name, String author, int price, String type) {
        this.name = name;
        this.author = author;
        this.price = price;
        this.type = type;
    }
}

注意哦布尔类型的isBorrowed,我们每次新存入一本书的时候,默认肯定是未借出的对吧,所以不写isBorrowed也可以。如果一定要写那每次就要多传一个参数false

2.2 提供公开方法

存书要使用数据的嘛,肯定得提供getter and setter

很简单

快捷添加

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getAuthor() {
    return author;
}

public void setAuthor(String author) {
    this.author = author;
}

public int getPrice() {
    return price;
}

public void setPrice(int price) {
    this.price = price;
}

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}

public boolean isBorrowed() {
    return isBorrowed;
}

public void setBorrowed(boolean borrowed) {
    isBorrowed = borrowed;
}

2.3 打印信息

存入了一本书,肯定要打印看看书的信息吧

也很简单

快捷添加toString()

@Override
public String toString() {
    return "Book{" +
            "name='" + name + '\'' +
            ", author='" + author + '\'' +
            ", price=" + price +
            ", type='" + type + '\'' +
            ", isBorrowed=" + isBorrowed +
            '}';
}

2.4 焯

通过上面几个步骤,可以看到书Book这个类就有了

就简单输入一点内容,idea快捷点几下

几十行代码就有了...

3.书架

前面写好了Book,接下来就是重点了

图书馆自然是有书架来管理

那么我们就可以使用单链表或者顺序表来充当这个书架,管理图书

这里就用顺序表来写,当然是写在book包里

3.1 书架数据

自然,我们需要空间来存放书,先简单来一点

private Book[] books = new Book[10];

书的数量也要记下

private int usedSize;

默认存书,得来一点书先存着对吧

public BookList() {
    books[0] = new Book("《HTML》", "我自", 11, "教育");
    books[1] = new Book("《CSS》", "己瞎", 45, "教育");
    books[2] = new Book("《JS》", "编的", 14, "教育");
    this.usedSize = 3;//3本书嘛
}

3.2 提供公开方法

书的数量应当提供getter and setter

public int getUsedSize() {
    return usedSize;
}

public void setUsedSize(int usedSize) {
    this.usedSize = usedSize;
}

找书,给个下标得需要找到这本书吧,写一个位置pos参数的getPos方法。不合法的情况就不管了

public Book getPos(int pos) {
    return this.books[pos];
}

存书,肯定得存书的吧

public void setBooks(int pos, Book book) {
    this.books[pos] = book;
}

4.用户

图书馆,图书管理系统,那当然是有用户来操作的

我们眼光放长远一些,说不定会有各种身份的人来使用

那我们直接写一个父类User,让其他身份的用户继承就好

User

public class User {
    protected String name;

    public User(String name) {
        this.name = name;
    }
}

当然是都需要姓名的,同类中要使用姓名,那protected即可

AdminUser

注意别忘了帮助父类构造构造方法

public class AdminUser extends User {
    public AdminUser(String name) {
        super(name);
    }
}

NormalUser

和前面一样

public class NormalUser extends User {
    public NormalUser(String name) {
        super(name);
    }
}

5.菜单

有了用户,那自然是要提供用户菜单

管理员菜单和普通用户菜单还不一样

要是普通用户也可以管理图书馆的核心内容,那要不了多久就会寄

而且也是需要指明操作功能才可以使用吧

写菜单也不难,我们直接写

管理员菜单

public void menu() {
    System.out.println("========== 管理员菜单 ==========");
    System.out.println("hello " + this.name + " 欢迎来到贝蒂的图书馆");
    System.out.println("    1.查找图书");
    System.out.println("    2.新增图书");
    System.out.println("    3.删除图书");
    System.out.println("    4.显示图书");
    System.out.println("    0.退出系统");
    System.out.println("==============================");
}

普通用户菜单

public void menu() {
    System.out.println("========== 管理员菜单 ==========");
    System.out.println("hello " + this.name + " 欢迎来到贝蒂的图书馆");
    System.out.println("    1.查找图书");
    System.out.println("    2.借阅图书");
    System.out.println("    3.归还图书");
    System.out.println("    0.退出系统");
    System.out.println("==============================");
}

6.登陆

写完菜单,就会发现其实少了个重要的功能

那就是登陆

合着压根就没登陆,根本就用不了

主函数

要进入程序,首先要登陆

那么程序的主入口,自然也是main函数

Main文件是独立于其他包的,也是整个程序入口的地方

public class Main {

}

首先要登陆,还要确认身份

写一个login方法

这里我们就直接把管理员和普通用户都列出来吧

public static User login() {
    System.out.println("请输入你的姓名:");
    Scanner scanner = new Scanner(System.in);
    String name = scanner.nextLine();
    System.out.println("请输入你的身份:");
    System.out.println("0.管理员 1.普通用户");
    int choice = scanner.nextInt();
    if (choice == 0) {
        return new AdminUser(name);
    } else {
        return new NormalUser(name);
    }
}

public static void main(String[] args) {
    User user = login();//向上转型
}

我们直接判断输入的数字

直接返回new一个用户类型

注意,这里main函数中的user引用,是不是返回那个用户类型就引用那个

但是!还要注意,User这个父类,他里面是没有菜单的

所以这时候使用user.一个menu(),必然是错误

这时,就要用到动态绑定了

打印菜单

现在User没有menu()user没得调用

解决方法也非常简单

前面不是在各身份用户中写好了菜单吗,而且都是User的子类

那我们就直接在User里写一个没内容的menu(),这样就相当于子类重写了menu(),发生了动态绑定

改法1:

public void menu() {
    
}

改法2:

直接改成抽象类

public abstract class User {
    public abstract void menu();
}

在这里我们使用方法2,改成抽象类,更加合适

现在再回到主函数,user就可以调用menu()

如果返回了管理员身份,就调用的是管理员菜单。如果返回普通用户就调用普通用户菜单

user.menu();//动态绑定

效果

idea:

其他终端(cmd、powershell、windows terminal):

7.选择(重要)

现在大致的系统框架有了

接下来,就是最重点的内容了

我们需要根据菜单选择功能

虽然现在功能没有实现,但是选择这一步,才是最难的一步

如何根据输入的数字调用合适的方法

7.1 输入

打印完菜单,那就要输入数字

我们写在两个用户的菜单里

Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();
return choice;

注意,现在是有返回值的,我们需要修改返回类型

需要修改所有menu()的返回值

7.2 接收

menu()返回了一个值

那么我们就接收

int choice = user.menu();

现在再来看这两行代码,就会觉得他们的意义很深重

User user = login();//向上转型
int choice = user.menu();//动态绑定

7.3 IOperation

注意!这一步很重要,对于具体功能实现,先超前涉及一点

我们需要一个接口,其他的具体功能可以看后面的Operation

public interface IOperation {
    public void work(BookList bookList);
}

来讲一下为什么要先创建一个接口?

注意图片中框起来的部分

想一下,操作这些功能的无非就是管理员和普通用户对吧

那如果后续,我们直接使用一个接口,是不是就可以想用哪个就直接调用?管理员用那个,普通用户用那个,是不是就可以分清了

非常巧妙,一个接口就可以调用全部

  • 这个接口中写一个方法work用来链接后面功能的实现
  • 每一个功能都会重写一个work,具体可以看后面的Operation,比如选择添加图书AddOperation会打印:新增图书!

这就是接口的好处,只需要在其他Operation功能implements这个接口即可

写这个接口,就是为了后面多态而做准备。如果看不太明白或者直接迷掉了,建议结合后面全部内容理解。

7.4 组装(很重要)

这一步,很重要

一步一步来看

我们现在有一个接口IOperation

直接用她在父类抽象类User里创建数组

protected IOperation[] iOperations;

那么我们再new出来AdminUserNormalUser

就可以对这个数组进行一个初始化,直接写在构造方法里

在我们打印菜单new出对象的时候,就把相对应的功能准备好放到IOperation数组当中了

AdminUser

public AdminUser(String name) {
    super(name);
    this.iOperations = new IOperation[]{
            new ExitOperation(),//0下标
            new FindOperation(),//1
            new AddOperation(),//2
            new DelOperation(),//3
            new DisplayOperation()//4
    };
}

NormalUser

public NormalUser(String name) {
    super(name);
    this.iOperations = new IOperation[]{
            new ExitOperation(),//0下标
            new FindOperation(),//1
            new BorrowOperation(),//2
            new ReturnOperation()//3
    };
}

7.5 链接

现在我们登陆进来会打印菜单,相对应用户身份的功能方法在IOperation数组当中

输入数字选择功能,也就是数组IOperation[]加上下标,然后调用前面写好的work方法就可以了。这个work会被后面的功能全部重写使用嘛

这一步在User中写一个方法doWork,因为无论有多少个子类用户身份,都是通过user来调用嘛

public void doWork(int choice, BookList bookList) {
    iOperations[choice].work(bookList);
}

main函数new一个前面写好的BookList,使用user调用doWork,传入参数choicebookList

BookList bookList = new BookList();
user.doWork(choice, bookList);

这个时候new出来个这个书架,默认是有3本书的嘛

7.6 效果(视频)

检验一下,可以写个死循环看看

while (true) {
    int choice = user.menu();//动态绑定
    //根据输入的choice来调用合适的方法
    user.doWork(choice, bookList);
}

8.Operation

最难的过去了,终于要开始实现功能了,大致是有

  1. 添加图书
  2. 借出图书
  3. 删除图书
  4. 显示图书
  5. 退出程序
  6. 查找图书
  7. 归还图书

这样的7个功能,当然其中有管理员和普通用户各自能操作的功能

一个一个来写,写成各自单独的文件,也就是面向对象(也可以放到顺序表中)

首先创建一个新的包,在里面创建这些文件

8.1 IOperation补充

前面先把IOperation写了,这里还可以再补充

后面的功能肯定涉及到了输入内容

那么我们干脆直接写在接口里

Scanner scanner = new Scanner(System.in);

8.2 AddOperation

public class AddOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("新增图书!");
    }
}

首先,work。主功能方法,每一个功能当然都有,也要调用的嘛

然后,添加书的话,需要输入一些信息吧

书名,作者,价格,类型

System.out.println("请输入书名:");
String name = scanner.nextLine();
System.out.println("请输入作者:");
String author = scanner.nextLine();
System.out.println("请输入价格:");
int price = scanner.nextInt();
System.out.println("请输入类型:");
String type = scanner.nextLine();

然后new出这本书,把参数都加上

获取顺序表也就是书架中存放书的末位置,usedSize嘛,用size接收上

放完后再加1

Book book = new Book(name, author, price, type);
int size = bookList.getUsedSize();
bookList.setBooks(size, book);
bookList.setUsedSize(size + 1);

就添加成功了

System.out.println("添加成功!");

效果

8.3 DisplayOperation

public class DisplayOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("打印图书!");
    }
}

得打印看看嘛

直接for循环打印,长度就是usedSize嘛,还是size接收

前面也有写好的getPos方法

int size = bookList.getUsedSize();
for (int i = 0; i < size; i++) {
    Book book = bookList.getPos(i);
    System.out.println(book);
}

效果

借出信息还是默认的显示

我们可以改一改

修缮

原本自动生成的toString方法就是字符串的拼接嘛

那我们稍微改一下就好

@Override
public String toString() {
    return "Book{" +
            "name='" + name + '\'' +
            ", author='" + author + '\'' +
            ", price=" + price +
            ", type='" + type + '\'' +
            ((isBorrowed == false) ? ", 未借出" : ", 已借出") +
            '}';
}

改一个三目运算符

测试一下

8.4 FindOperation

根据书名查找嘛

这里就不写的太复杂了,简单写一下

public class FindOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("查找图书!");
        System.out.println("请输入要查找的书名:");
        String name = scanner.nextLine();
        int size = bookList.getUsedSize();
        for (int i = 0; i < size; i++) {
            Book book = bookList.getPos(i);
            if (name.equals(book.getName())) {
                System.out.println("找到了!信息如下:");
                System.out.println(book);
                return;
            }
        }
        System.out.println("没有找到这本书!请速速添加进来。");
    }
}

只涉及到了一个字符串的比较

效果

8.5 ExitOperation

退出系统就最简单了

正常的退出code码就是0嘛

public class ExitOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("退出系统!");
        System.exit(0);
    }
}

效果

8.6 DelOperation

public class DelOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("删除图书!");
        //1.和查找一样,先定位下标找到位置
        System.out.println("请输入要删除的书名:");
        String name = scanner.nextLine();
        int currentSize = bookList.getUsedSize();//长度接收一下
        int index = 0;//存储找到书的下标
        int i = 0;
        for (; i < currentSize; i++) {
            Book book = bookList.getPos(i);
            if (book.getName().equals(name)) {
                index = i;
                break;
            }
        }
        if (i >= currentSize) {
            System.out.println("没有找到这本书!那就不用删喽~");
            return;
        }
        //2.走到这里那就是找到了,进行一个删除
        for (int j = index; j < currentSize-1; j++) {
            Book book = bookList.getPos(j + 1);
            bookList.setBooks(j, book);
        }
        bookList.setBooks(currentSize, null);
        bookList.setUsedSize(currentSize - 1);
        System.out.println("删除成功!");
    }
}

注意bookList是一个类,不可以像数组一样直接j = j + 1

最后j走完记得置空,长度减1

效果

8.7 BorrowOperation

public class BorrowOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("借阅图书!");
        System.out.println("请输入要借阅的书名:");
        String name = scanner.nextLine();
        int size = bookList.getUsedSize();
        for (int i = 0; i < size; i++) {
            Book book = bookList.getPos(i);
            if (name.equals(book.getName())) {
                book.setBorrowed(true);
                System.out.println("借阅成功,记得归还哦!");
                return;
            }
        }
        System.out.println("没有找到这本书!请联系管理员。");
    }
}

还是先找,找到了改成已借出

效果

8.8 ReturnOperation

public class ReturnOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("归还图书!");
        System.out.println("请输入要归还的书名:");
        String name = scanner.nextLine();
        int size = bookList.getUsedSize();
        for (int i = 0; i < size; i++) {
            Book book = bookList.getPos(i);
            if (name.equals(book.getName())) {
                book.setBorrowed(false);
                System.out.println("归还成功,下次再借哦!");
                return;
            }
        }
        System.out.println("这本书不是本图书馆的!请联系管理员。");
    }
}

这个就简单了,借阅图书反转一下就好

效果

9总结

通过这个图书管理系统的练习,可以很好的巩固知识并且理解知识

其中的多态,还有借口的使用,是最难的部分

仅仅一两行代码,却有那么多意义

等后面讲完数据库后,就可以把书的信息存放在数据库当中了

就像我现在的博客,其中的所有文章内容等都存储在数据库和服务器文件中。

小头图版权:《繋がる青い糸》by 京田スズカ 2021年12月7日晚上11点00分 pid:94635088

广告位招租
最后修改:2021 年 12 月 09 日 08 : 24 PM
如果觉得我的文章对你有用,请喂饱我!(理直气壮)