Java实现 简易图书管理系统(万字详解)

设计背景

        图书馆的藏书成千上万,没有一个合适的图书管理系统且不是乱套了?如果没有系统的管理更显得杂乱无章,使人困扰。因此,一个能够系统管理书籍图书管理系统就显的尤为重要了🤭

利用知识

        类和对象、抽象类和接口、继承和多态、封装、数组、重写等🖊

设计思路

        ①区分普通用户与管理员,不同用户所展示的操作界面不同 => 通过继承和多态实现

        ②为了程序的安全性 => 使用封装将各种操作封装成一个个类

           遵守程序规范 => 使用接口定义规范

        ③有几个对象设计几个类 => 书类、用户类(这两个是最容易想到的)、功能类,以此为基础再去细分类(如图)

        ④读者能够实现查找书籍,借阅书籍以及归还书籍 => 可利用方法的重写与多态 更加高效

③中对类的简分(这个图后面还会用到)

         了解到这里,我们基本了解了图书管理系统的主要组成部分,并且可以开始着手编译了~

具体类实现与模块展示

        0.包的创建

        在这个程序中,为了实现继承、多态与封装,将会创建很多个类。为了方便管理以及debug,我们可以创建三个包,分别放相关的类(严格执行一类一坑位🐕!)

        

        这样一来,代码的逻辑性与可读性都会大大增加~更重要的是方便我们后期的增删添改👍

(进入正题)

book包——

        1.Book类

        书籍的属性应该有哪些?书名、作者、价格、类型、是否被借出。结合封装性,我们可以得到Book类的几个对象:

public class Book {
    private String name;
    private String author;
    private int price;
    private String type;
    private boolean isBorrowed;
}

        此外,我们继续提供相应的get方法set方法空参构造器,以及一个不包含是否被借出的构造器(布尔值的默认类型就是false,可以不进行初始化)并按需提供对应的toString()方法重写

//Book类的完整创建

package book;

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;
    }

    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;
    }

    @Override  //下文的ShowOperation类和FindOperation类
               //将会使用到此重写方法!

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

(注意:上段代码大部分由编译器生成,只需输入书的属性,然后运用Getter and setter构造方法 以及toString方法的快捷键,就能快速生成上述代码✌✌

        2.BookList类(书架)

        Book创建了之后要有地方存储,BookList类主要为我们提供了一个数组进行存储,以及对以占用空间的记录。这里我们为useSize和books提供get和set方法。注意这里的getBooks和setBooks方法需要一个int类型变量。因为后面我们需要根据这个变量来访问books数组中的元素~

        booklist存在的意义约等于书架,是存放书类型的数组😐

//BookList类的完整创建

package book;

public class BookList {
    private Book[] books;
    private int useSize;

    public BookList() {
        this.books = new Book[10];
        this.books[0] = new Book("三国演义","罗贯中",10,"小说");
        this.books[1] = new Book("西游记","吴承恩",9,"小说");
        this.books[2] = new Book("红楼梦","曹雪芹",19,"小说");
        this.useSize = 3;
    }

    public Book getBooks(int pos) {
        return books[pos];
    }

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

    public int getUseSize() {
        return useSize;
    }

    public void setUseSize(int useSize) {
        this.useSize = useSize;
    }
}

        我们先创建三个Book对象放到Book数组中,以便后面我们调试~

user包——

        1.User类

        还记得前面导图中的用户类吗?里面有两种用户,一个是管理员,一个是普通用户,从想实现的内容来看,两种用户都有各自的菜单,同时他们都有姓名等一些公共的属性:

所以这里我们可以定义一个User父类,让AdminUser和 NormalUser去继承这个类。

        重点来了!为了能根据choice调用不同的操作,这个方法自然也应该在user这个类中。故我们先在User内部定义一个成员变量 => 操作接口数组,用于存放不同的操作类

        因为两者菜单不同,所以在父类User中不需要有具体的menu方法,故用abstract修饰,而菜单需要返回用户输入的值,故用int定义🖊

//User的完整实现

package user;

import book.BookList;
import operation.IOperation;

public abstract class User {
    protected String name;
    protected IOperation[] ioperations; // 创建操作接口的数组
    public User(String name) {
        this.name = name;
    }
    public void doOperation(int choice, BookList bookList) {
        this.ioperations[choice].work(bookList); // 通过choice返回的值,调用不同的方法
    }
    public abstract int menu();
}

        2.AdminUser类 & NormalUser类

        管理员类和普通用户类继承User类,对菜单进行重写,根据两种用户各自所需的功能进行打印,最后定义一个choice来接收~

//AdminUser类的完整实现

package user;

import operation.*;

import java.util.Scanner;

public class AdminUser extends User {
    public AdminUser(String name) {
        super(name);
        this.ioperations = new IOperation[]{ // 初始化数组(下文详细讲解)
                new ExitOperation(),
                new FindOperation(),
                new AddOperation(),
                new DelOperation(),
                new ShowOperation()
        };
    }

    public int menu() {
        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("请输入你的操作:");
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();
        return choice;
    }
}

        是不是没看懂?  猜得出来,因为我一开始也没看懂XD   

        没关系,下面三个Q&A应该能解决你的疑惑~

---------------------------------------------------------------------------------------------------------------------------------

Q1:为什么这里创建了一个数组?

        A:因为两种用户所要实现的功能不同,导致两种类需要调用的方法不同,为了让两种用户都能正确地调用自己的方法,我们在父类 User 定义一个成员变量 IOperation数组 ,然后在 User 的子类 AdminUser 和 NormalUser上分别对他进行实例化

Q2: 为什么这个数组能根据choice返回的值来调用不同的操作?

        A:我们回到父类User中,看一下这个方法:

public void doOperation(int choice, BookList bookList) {
        this.ioperations[choice].work(bookList);
    }

              我们通过this.ioperations[choice] 来获取一个在某处定义的 IOperation 数组中的元素IOperation 是一个接口,这里数组中的元素实现了这个接口的类的实例。                                (说人话:调用ioperations数组下标为choice的元素,实现其work方法)

Q3:怎么保证输入的值和操作相对应?

        A:这个地方比较巧妙,我们通过数组的特性来实现。在这个菜单中,用户只需输入对应的数字即可调用相关方法。

        我们不难看出,菜单对应的操作是和数组元素的顺序是相同的,如果在菜单中输入1,就会调用ioperations[1],也就是FindOperation()。

-------------------------------------------------------------------------------------------------------------------------------- 

        NormalUser的实现类似与AdminUser,只是创建的数组稍有不同~

//NormalUser类的完整实现

package user;

import operation.*;

import java.util.Scanner;

public class NormalUser extends User{
    public NormalUser(String name){
        super(name);
        this.ioperations = new IOperation[]{
                new ExitOperation(),
                new FindOperation(),
                new BorrowOperation(),
                new ReturnOperation()
        };
    }

    public int menu() {
        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("请输入你的操作:");
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();
        return choice;
    }
}

Main类——

        用户和菜单都有了,此时我们需要一个类把他们全都整合起来。

        接下来我们就需要登录了,我们写一个main方法,其中写一个login方法,先写判断判定 user 指向哪个对象 (向上转型) 

        然后可以打印指定的菜单,通过返回的choice来调用 user 指向对象的操作方法(动态绑定)

// Main类的完整实现

import book.BookList;
import user.AdminUser;
import user.NormalUser;
import user.User;

import java.util.Scanner;

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


    public static void main(String[] args) {
        BookList bookList = new BookList();
        User user = login();
        while (true) {
            int choice = user.menu();
            user.doOperation(choice,bookList);
        }
    }
}

operation包——

        1.IOperation类

        熟悉接口的朋友应该已经看出来了,这里要将操作类实现为操作接口🎇   

        因为无论是管理员还是普通用户,我们需要实现的所有功能都属于对书的操作,都是在BookList类的数组books中操作的同时重写的方法提供书架类,便于进行操作~

//Ioperation接口的完整实现

package operation;

import book.BookList;

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

        接下来就可以根据接口来实现类了⚔ ⚔

        2.ExitOperation类(退出系统)

        我们先从最简单的操作类入手,利用system.exit(int status),status为0时为正常退出程序,也就是结束当前正在运行中的java虚拟机。

package operation;

import book.BookList;

public class ExitOperation implements IOperation {
    public void work(BookList bookList) {
        //其实还应该要对 bookList 资源 手动回收
        System.out.println("退出系统...");
        System.exit(0);
    }
}

        3.ShowOperation类(显示图书)

        这个也比较简单,先获取BookList类中 bookList中实际使用的元素数量getUseSize(),然后使用for循环遍历bookList数组,获取当前索引(i)处的book对象并打印✌

//ShowOperation类的完整实现

package operation;

import book.Book;
import book.BookList;

public class ShowOperation implements IOperation {
    public void work(BookList bookList) {
        System.out.println("显示图书!");
        int currentSize = bookList.getUseSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            System.out.println(book);
            // 因为重写了tostring方法,所以此处能打印我们想要输出的格式
            // (具体见Book类 完整实现)
        }
    }
}

        4.AddOperation类(新增图书)

        这个类主要分为两个部分:①判断要新增的书是否已在书架中

                                                   ②将要新增的书放入书架

        对于第一部分,我们使用for循环遍历整个bookList数组,查找是否有相同name的元素:

        //判断新增书是否在bookList中

    public void work(BookList bookList) {
        System.out.println("新增图书!");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要新增的书名: ");
        String name = scanner.nextLine();

        int currentSize = bookList.getUseSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            if (book.getName().equals(name)) {
                System.out.println("此书已存在!");
                return;
            }
        }

        若没有,则将新建一个book对象并赋值,将book放入bookList数组中,同时更新占用的空间~

//AddOperation类的完整实现

package operation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class AddOperation implements IOperation{

    public void work(BookList bookList) {
        System.out.println("新增图书!");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要新增的书名: ");
        String name = scanner.nextLine();

        //判断新增书是否在bookList中

        int currentSize = bookList.getUseSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            if (book.getName().equals(name)) {
                System.out.println("此书已存在!");
                return;
            }
        }

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

        //将新增书放入bookList数组中

        Book book = new Book(name, author, price, type);
        bookList.setBooks(book,currentSize);
        bookList.setUseSize(currentSize+1);
        System.out.println("设置成功!");
    }
}

5.DelOperation类(删除图书)

        有增加就有删除。此处我们实现的删除方法是:用要删除元素后一个的元素覆盖掉要删除的元素,以此类推(相当于向前推进了一个元素,要删除元素被“挤掉”了),因此多了一个index变量来储存要删除元素的下标,以便后续的覆盖操作精准进行~

        bookList.setBooks(null,currentSize-1);

        👆此处要注意,最后一个元素无法被覆盖(否则溢出),故我们将其置为null。(必须将其置为null!这里涉及到回收,不过多赘述)

//DelOperation类的完整实现

package operation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class DelOperation implements IOperation {
    public void work(BookList bookList) {
        System.out.println("删除图书!");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要删除的书名: ");
        String name = scanner.nextLine();

        int currentSize = bookList.getUseSize();
        int index = -1;

        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            if (book.getName().equals(name)) {
                index = i;
            }
        }

        if (index == -1) {
            System.out.println("无法删除, 此书不存在!");
        }

        for (int j = index; j < currentSize-1; j++) {
            Book book = bookList.getBooks(j+1);
            bookList.setBooks(book,j);
        }

        bookList.setBooks(null,currentSize-1);
        bookList.setUseSize(currentSize-1);
        System.out.println("删除成功!");
    }
}

        这两个操作类搞定之后,剩下几个类的就比较简单了🙌

6.BorrowOperation类(借阅图书)

        利用book对象的布尔值(isBorrowed)来判断或改变📕借阅状态~

package operation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class BorrowOperation implements IOperation {
    public void work(BookList bookList) {
        System.out.println("借阅图书!");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要借阅的书名: ");
        String name = scanner.nextLine();

        int currentSize = bookList.getUseSize();

        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            if (book.getName().equals(name)) {
                if (!book.isBorrowed()) {
                    book.setBorrowed(true); //设置为以借阅
                    System.out.println("成功借阅!");
                    return;
                }
            }
        }
        System.out.println("抱歉, 暂无此书: "+name);
    }
}

7.ReturnOperation类(归还图书)

        和借阅图书类似,改变book对象的布尔值(isBorrowed)来改变借阅状态👊

package operation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class ReturnOperation implements IOperation{
    public void work(BookList bookList) {
        System.out.println("归还图书!");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要归还的书名: ");
        String name = scanner.nextLine();

        int currentSize = bookList.getUseSize();

        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            if (book.getName().equals(name)) {
                book.setBorrowed(false);
                System.out.println("成功归还!");
                return;
                }
            }
        System.out.println("抱歉, 此书不在归还范围中:"+name);
    }
}

8.FindOperation(查找图书)

        for遍历即可,每一次遍历先定义一个book对象,然后比较该对象的名称与输入的名称是否一样,如果找到了,提示并打印🖨

package operation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class FindOperation implements IOperation {
    public void work(BookList bookList) {
        System.out.println("查找图书!");
        System.out.println("请输入要查找的书名:");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();

        int currentSize = bookList.getUseSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBooks(i);
            if (book.getName().equals(name)) {
                System.out.println("存在这本书,信息如下:");
                System.out.println(book);
                return;
            }
        }
        System.out.println(name+"此书不存在!");
    }
}
         现在万事俱备,一运行就能达到我们想要的效果啦!💪

代码总览

        如果要查看代码,点击下方链接跳转至我的gitee仓库即可浏览~

        简易的Java图书管理系统——点击传送

结语

        笔者是一个Java初学者,很多知识点都是了解一些皮毛,也是想借着写博客的机会巩固自己学习过的知识,如果有错误请大佬们狠喷!

        我们!🎇🎇🎇

  • 35
    点赞
  • 187
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值