Bootstrap

基于Java基础的图书管理系统

前言


时隔两周,狗子我再次写起了 Java 练手案例——“图书管理系统”,这是一个精简版的图书管理系统,等后面自己技术上去了估计会进一步开发,觉得应该不会很久。如果在阅读过程中有什么问题欢迎评论留言呀!
在这里插入图片描述

之前案例的文章有兴趣的小伙伴可以自行跳转去查看呀!里面都附有对应的源码:

一、前期准备


在开始搞事情之前得先准备好下面这些东西,其中 JDK 和 IDE 不一定需要统一,这只是狗子我使用的版本而已。准备好之后带上我们的脑子就可以开搞啦

  • 编程语言 : Java
  • 运行环境 : JDK1.8
  • IDE : IDEA 2020
  • 储备知识 : Java基础

二、需求分析


在这次案例中分成了管理员端以及普通用户端,两者的功能权限各不相同。

  1. 管理员
功能模块功能分析备注
查看图书用于用户查看目前馆中所存在的所有书籍不可裁剪
添加图书用于管理员新增馆中书籍不可裁剪
删除图书用于管理员删除馆中书籍不可裁剪
修改图书用于管理员修改馆中存在书籍的基本信息不可裁剪
退出功能用于用户退出当前账户不可裁剪
  1. 普通用户
功能模块功能分析备注
查看图书用于用户查看目前馆中所存在的所有书籍不可裁剪
借阅图书用于用户借阅馆中所存在的书籍不可裁剪
归还图书用于用户归还已借阅的图书不可裁剪
退出功能用于用户退出当前账户不可裁剪

与此同时,附上本案例的结构图(本狗起草,画的不好还请见谅)
结构

三、核心代码开发


在这次案例中和之前的案例一样,同样使用到了 MVC框架 ,将业务层和视图层进行分离,提高了程序的可维护性,降低了耦合性。如果对 MVC设计模式 存在不理解得到,可直接跳转 百度百科 进行查看,或去 CSDN 的搜索框内查找其他人的经验之谈

1、 model 层


1.1、 Book 类

这是封装了有关图书的类,便于以后的查找和修改,其封装内容如下,分别为图书书名、图书作者、图书出版社、图书馆藏位置、图书借阅状况以及仿造 中国标准ISBN码 设定的 ISBNCode 变量,提供对应的构造器并在本类中重写了 toString() 方法来展示图书信息。

public class Book {
    private String name;        //书名
    private String author;      //作者
    private String publisher;   //出版社
    private String address;     //馆藏位置
    private String status;      //借阅状况
    private String ISBNCode;    //中国大陆标准ISBN码

    public Book() {

    }

    public Book(String name, String author, String publisher, String address, String status) {
        this.name = name;
        this.author = author;
        this.publisher = publisher;
        this.address = address;
        this.status = status;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", publisher='" + publisher + '\'' +
                ", address='" + address + '\'' +
                ", status='" + status + '\'' +
                ", ISBNCode='" + ISBNCode + '\'' +
                '}';
    }

    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 String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getISBNCode() {
        return ISBNCode;
    }

    public void setISBNCode(String ISBNCode) {
        this.ISBNCode = ISBNCode;
    }
}

1.2、 User 类

创建一个 User 用户抽象类,里面封装了 用户名、操作对象数组、构造方法以及 menu 菜单,通过menu菜单返回的选项调用操作对象数组中对应的 opera 方法

public abstract class User{
    protected String name;                  //用户名
    protected IOOperation[] operations;     //操作对象数组

    public User() {

    }

    public User(String name) {
        this.name = name;
    }
    /**
    * @Description 定义菜单抽象方法,等待该抽象类的子类对其进行重写
    * @Date 20:13 2021/10/5
    * @Param []
    * @return int
    **/
    abstract public int menu();

    /**
    * @Description 通过用户选择,调用对应对象中的 opera 方法
    * @Date 20:11 2021/10/5
    * @Param [selection, bookList]
    * @return void
    **/
    public void doOperation(int selection, BookList bookList) {
        operations[selection].opera(bookList);
    }
}

1.3、 NormalUser 类

创建 NormalUser 普通对象类,其继承了 User 抽象类,在构造器中实例化普通用户可以使用的功能,并重写了 User 抽象类里面的 menu 抽象方法。这里需要注意的是:实例化 operation 操作对象数组的时候不能在无参构造器中进行实例化如果你需要使用有参构造器的话,因为调用有参构造器的时候会调用父类的有参构造器,这样便不能同时隐式调用自己的无参构造器了。

public class NormalUser extends User{
    /**
    * @Description 利用构造器,实例化普通用户可以使用的功能
    * @Date 20:22 2021/10/5
    * @Param []
    * @return
    **/
    public NormalUser(String name) {
        super(name);
        this.operations = new IOOperation[] {
                new ExitSystem(),
                new ListAllBook(),
                new BorrowBook(),
                new ReturnBook()
        };
    }

    @Override
    public int 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("=============================");
        System.out.println("请输入你的选择");
        return LMSUtility.readMenuSelection(3);
    }
}

1.4、 Admin 类

创建 Admin 普通对象类,其继承了 User 抽象类,在构造器中实例化普通用户可以使用的功能,并重写了 User 抽象类里面的 menu 抽象方法。需要注意的点和 普通用户类 一样,实例化 operation 操作对象数组的时候不能在无参构造器中进行实例化如果你需要使用有参构造器的话

public class Admin extends User{
    /**
     * @Description 利用构造器,实例化普通用户可以使用的功能
     * @Date 20:22 2021/10/5
     * @Param []
     * @return
     **/
    public Admin(String name) {
        super(name);
        //下面这一代码块不能写在无参构造器中,否则不能执行,导致 operations 为空
        this.operations = new IOOperation[] {
                new ExitSystem(),
                new ListAllBook(),
                new AddBook(),
                new DeleteBook(),
                new ReplaceBook()
        };
    }
    @Override
    public int menu() {
        Scanner sc = new Scanner(System.in);
        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("=============================");
        System.out.println("请输入你的选择");
        int selection = new Scanner(System.in).nextInt();
        return selection;
    }
}

2、 service 层


2.1、 IOOperation 接口

创建一个用户操作接口 IOOperation,让下面所有操作功能分别用类进行包装来实现该接口,从而达到目标功能的效果

public interface IOOperation{
    public void opera(BookList books);
}

2.1、 AddBook 类

创建 添加图书类 AddBook,实现了 IOOperation 接口

public class AddBook implements IOOperation{
    @Override
    public void opera(BookList books){
        System.out.print("请输入书籍名称:");
        String name = LMSUtility.readString(30);
        System.out.print("请输入书籍作者:");
        String author = LMSUtility.readString(20);
        System.out.print("请输入书籍出版社:");
        String publisher = LMSUtility.readString(32);
        System.out.print("请输入书籍馆藏地址:");
        String address = LMSUtility.readString(24);

        Book book = new Book(name, author, publisher, address, "free");
        try {
            books.addBook(book);
        } catch (BookException e) {
            System.out.println("出错了!" + e.getMessage());
        }
        System.out.println("添加成功!");
    }
}

2.2、 DeleteBook 类

创建 删除图书类 DeleteBook,实现了 IOOperation 接口

public class DeleteBook implements IOOperation{
    @Override
    public void opera(BookList books){
        System.out.print("请输入需要删除书籍的ISBN码:");
        String ISBN = LMSUtility.readString(20);
        try {
            books.deleteBook(ISBN);
        } catch (BookException e) {
            System.out.println("出错了!" + e.getMessage());
        }
    }
}

2.3、 ReplaceBook 类

创建 修改图书类 ReplaceBook,实现了 IOOperation 接口

public class ReplaceBook implements IOOperation{
    @Override
    public void opera(BookList books) {
        if(books.getBookNumber() == 0) {
            try {
                throw new BookException("馆中目前不存在任何书籍");
            } catch (BookException e) {
                System.out.println("出错了!" + e.getMessage());
            }
        }
        System.out.print("请输入需要进行修改书籍的ISBN码:");
        String ISBN = LMSUtility.readString(20);

        Book[] bookList = books.getBookList();
        for (int i = 0; i < books.getBookNumber(); i++) {
            if(bookList[i].getISBNCode().equals(ISBN)) {
                //修改书籍信息,支持回车跳过,保留原信息不变
                System.out.print("请输入修改后书名(" + bookList[i].getName() + "):");
                String name = LMSUtility.readString(30,bookList[i].getName());
                System.out.print("请输入修改后作者(" + bookList[i].getAuthor() + "):");
                String author = LMSUtility.readString(20,bookList[i].getAuthor());
                System.out.print("请输入修改后出版社(" + bookList[i].getPublisher() + "):");
                String publisher = LMSUtility.readString(32,bookList[i].getPublisher());
                System.out.print("请输入修改后馆藏地址(" + bookList[i].getAddress() + "):");
                String address = LMSUtility.readString(24,bookList[i].getAddress());

                Book modifiedBook = new Book(name, author, publisher, address, bookList[i].getStatus());    //新建书籍对象
                modifiedBook.setISBNCode(bookList[i].getISBNCode());    //将 ISBN 码直接赋值过去
                bookList[i] = modifiedBook;     //将新建的书籍对象覆盖原对象
                System.out.println("修改成功");
                return;
            }
        }
        try {
            throw new BookException("ISBN码为 \"" + ISBN + "\" 的书籍不存在,无法修改");
        } catch (BookException e) {
            System.out.println("出错了!" + e.getMessage());
        }
    }
}

2.4、 ListAllBook 类

创建 展示图书类 ListAllBook,实现了 IOOperation 接口

public class ListAllBook implements IOOperation{
    @Override
    public void opera(BookList books){
        Book[] allBook = books.getBookList();
        System.out.println("\n============================书籍信息============================\n");
        for (int i = 0; i < books.getBookNumber(); i++) {
            System.out.println(allBook[i].toString());
        }
        System.out.println("\n===============================================================\n");
    }
}

2.5、 BorrowBook 类

创建 借阅图书类 BorrowBook,实现了 IOOperation 接口

public class BorrowBook implements IOOperation{
    @Override
    public void opera(BookList books) {
        if(books.getBookNumber() == 0) {
            try {
                throw new BookException("馆中目前不存在任何书籍");
            } catch (BookException e) {
                System.out.println("出错了!" + e.getMessage());
            }
        }
        System.out.print("请输入需要进行租借书籍的ISBN码:");
        String ISBN = LMSUtility.readString(20);
        //遍历搜索书籍
        Book[] bookList = books.getBookList();
        for (int i = 0; i < books.getBookNumber(); i++) {
            if (bookList[i].getISBNCode().equals(ISBN)) {
                //书籍已被借出
                if(bookList[i].getStatus().equals("borrowed")) {
                    try {
                        throw new BookException("很抱歉,书籍已借出");
                    } catch (BookException e) {
                        System.out.println(e.getMessage());
                    }
                    return;
                }
                //设立确认环节
                System.out.print("是否确认租借《" + bookList[i].getName() + "》(Y/N)");
                char confirm = LMSUtility.readConfirmSelection();
                if(confirm == 'N') {
                    return;
                }
                bookList[i].setStatus("borrowed");
                System.out.println("租借成功");
            }
        }
        //书籍不存在
        try {
            throw new BookException("ISBN码为 \"" + ISBN + "\" 的书籍不存在,无法租借");
        } catch (BookException e) {
            System.out.println("出错了!" + e.getMessage());
        }
    }
}

2.6、 ReturnBook 类

创建 归还图书类 ReturnBook,实现了 IOOperation 接口

public class ReturnBook implements IOOperation{
    @Override
    public void opera(BookList books) {
        System.out.print("请输入要归还书籍的ISBN码:");
        String ISBN = LMSUtility.readString(20);
        //遍历书籍
        Book[] bookList = books.getBookList();
        for (int i = 0; i < books.getBookNumber(); i++) {
            if (bookList[i].getISBNCode().equals(ISBN)) {
                //书籍仍在馆中
                if(bookList[i].getStatus().equals("free")) {
                    try {
                        throw new BookException("这本书籍未被借阅,无需归还");
                    } catch (BookException e) {
                        System.out.println("出错了!" + e.getMessage());
                    }
                }
                //设立确认环节
                System.out.print("是否确认归还《" + bookList[i].getName() + "》(Y/N)");
                char confirm = LMSUtility.readConfirmSelection();
                if(confirm == 'N') {
                    return;
                }
                bookList[i].setStatus("free");
                System.out.println("归还成功");
                return;
            }
        }
        //书籍不存在,抛出异常
        try {
            throw new BookException("ISBN码为 \"" + ISBN + "\" 的书籍不存在,无法归还");
        } catch (BookException e) {
            System.out.println("出错了!" + e.getMessage());
        }
    }
}

2.7、 ExitSystem 类

创建 退出账户类 ExitSystem ,实现了 IOOperation 接口

public class ExitSystem implements IOOperation{
    @Override
    public void opera(BookList books) {
        System.out.print("是否确认退出账号(Y/N)");
        char confirm = LMSUtility.readConfirmSelection();
        if(confirm == 'N') {
            return;
        }
        System.out.println("退出成功");
    }
}

3、 view 层

3.1、 LMSUtility 类

创建 TSUtility 工具类,封装了众多读取键盘数据并进行处理的方法,方便我们实现键盘的访问。(其实是为了偷懒,直接复制了之前的工具类进行了微调)

public class LMSUtility {
    private static Scanner sc = new Scanner(System.in);

    /**
     * @Description 用于界面菜单的选择,返回类型为整形,返回值为用户输入的选项
     * @Date 12:25 2021/8/29
     * @Param [maxSelection]
     * @return int
     **/
    public static int readMenuSelection(int maxSelection) {
        while(true) {
            int selection = 0;
            try {
                selection = Integer.parseInt(readKeyBoard(1,false));
            } catch (NumberFormatException e) {
                System.out.print("输入选项有误,请重新输入:");
                continue;
            }
            if(selection < 0 || selection > maxSelection) {
                System.out.print("输入选项有误,请重新输入:");
                continue;
            }
            return selection;
        }
    }
    /**
     * @Description 读取一个字符,且不允许输入空,并将其作为方法的返回值
     * @Date 16:56 2021/8/29
     * @Param []
     * @return char
     **/
    public static char readChar() {
        return readKeyBoard(1, false).charAt(0);
    }
    /**
     * @Description 读取一个字符,且允许输入空,若直接回车没有输入数据则返回默认值 defaultValue
     * @Date 20:51 2021/8/29
     * @Param [defaultValue]
     * @return char
     **/
    public static char readChar(char defaultValue) {
        String str = readKeyBoard(1, true);
        return str.length() == 0 ? defaultValue : str.charAt(0);
    }
    /**
     * @Description 读取一个不超过二位数的整数,且不允许输入空
     * @Date 16:51 2021/8/29
     * @Param []
     * @return int
     **/
    public static int readInt() {
        int number;
        while(true) {
            try {
                number = Integer.parseInt(readKeyBoard(2, false));
                break;
            } catch (NumberFormatException e) {
                System.out.print("数字输入错误,请重新输入:");
            }
        }
        return number;
    }
    /**
     * @Description 读取一个不超过二位数的整数,且允许输入空,若没有输入数据直接回车则返回 defaultValue
     * @Date 16:39 2021/8/29
     * @Param [defaultValue]指定默认返回值
     * @return int
     **/
    public static int readInt(int defaultValue) {
        int number;
        while(true) {
            String str = readKeyBoard(2, true);
            try {
                number = str.equals("") ? defaultValue : Integer.parseInt(str);
                break;
            } catch (NumberFormatException e) {
                System.out.print("数字输入错误,请重新输入:");
            }
        }
        return number;
    }
    /**
     * @Description 读取一个长度不超过 limit 的字符串,且不允许输入空
     * @Date 20:54 2021/8/29
     * @Param [limit]
     * @return String
     **/
    public static String readString(int limit) {
        return readKeyBoard(limit, false);
    }
    /**
     * @Description 读取一个长度不超过 limit 的字符串,且允许输入空,若没有输入数据直接回车则返回 defaultValue
     * @Date 21:00 2021/8/29
     * @Param [limit, defaultValue]
     * @return java.lang.String
     **/
    public static String readString(int limit, String defaultValue) {
        String str = readKeyBoard(limit, true);
        return str.equals("") ? defaultValue : str;
    }
    /**
     * @Description 用于确认选择的输入,读取‘Y’或‘N’,并将其作为返回值
     * @Date 21:06 2021/8/29
     * @Param []
     * @return char
     **/
    public static char readConfirmSelection() {
        while(true) {
            char ch = readKeyBoard(1, false).toUpperCase().charAt(0);
            if(ch == 'Y' || ch == 'N') {
                return ch;
            } else {
                System.out.print("选择错误,请重新选择:");
            }
        }
    }
    /**
    * @Description 从键盘读取限长 limit 的字符串并只限于本类中调用,返回值为该字符串
    * @Date 22:34 2021/10/4
    * @Param [limit, blankReturn] blankReturn为 true,则可返回空串,否则必须输入非空串
    * @return java.lang.String
    **/
    private static String readKeyBoard(int limit, boolean blankReturn) {
        String line = "";
        while(sc.hasNextLine()) {
            line = sc.nextLine();
            if(line.length() == 0) {
                if(blankReturn) {
                    return line;
                } else {
                    continue;
                }
            }
            if (line.length() < 1 || line.length() > limit) {
                System.out.print("长度应不大于 " + limit + ", 请重新输入:");
                continue;
            }
            break;
        }
        return line;
    }
}

3.2、 MainText 类

创建主测试类,对前面开发的所有代码进行一个总的测试

public class MainText {
    public static void main(String[] args) {
        BookList bookList = new BookList();
        User user = login();
        while(true) {
            //判断是否退出程序
            if(user == null) {
                break;
            }
            int selection = user.menu();
            //对退出账号进行处理
            if(selection == 0) {
                user = login();
                continue;
            }
            user.doOperation(selection, bookList);
        }
    }
    /**
    * @Description 用于用户登陆
    * @Date 16:08 2021/10/6
    * @Param []
    * @return com.atfunny.LibraryMS.javabean.User
    **/
    public static User login() {
        System.out.println("1. 普通用户\t\t2. 管理员");
        System.out.println("0. 退出系统");
        System.out.print("请进行您的选择:");
        int selection = LMSUtility.readMenuSelection(2);
        if(selection == 0) {
            return null;
        }
        System.out.print("请输入您的姓名:");
        String name = LMSUtility.readString(10);
        return selection == 1 ? new NormalUser(name) : new Admin(name);
    }
}

四、测试结果

1、 管理员

管理员

2、 普通用户

普通用户

五、总结

写完这一案例,还是能有一些东西进行总结的。狗子我在下面进行一一列举

  1. 再次证明了动手敲代码真的很重要。时隔两周没怎么动过 Java (最近在学 MySQL),很明显能感觉的出来已经有点生疏了
  2. 这次的案例是根据 Java 项目中老掉牙的图书管理系统进行修改的一个精简版,很多地方是不够完善得到,单纯只是为了进行一个练习。感兴趣的大家伙们可以考虑一下对其进行完善,如利用 ArrayList 来封装不同的用户,在登录界面上进行判断识别等

源码已更新到了 gitee 上,需要的小伙伴可自行去查看 – > Go ! ! !

在这里插入图片描述

;