Java基础——实现图书管理系统交互功能

发布于:2025-08-03 ⋅ 阅读:(10) ⋅ 点赞:(0)

        众所周知,图书馆里得先有一定数量的书,而书本放在书架上是有编号的,然后有管理员整理这些书架上的图书,才会有顾客来看书、借书和还书。而图书管理系统就是面向用户,方便用户对图书进行操作的一种交互式系统,此处也就体现了面向对象编程的特点。下图是本文的编写依据

        准备工作:在社区版IDEA中新建一个空项目,在 src 文件夹下新建一个名为 book 的包,用来存放图书和书架两个实体类;再新建一个名为 user 的包,用来存放用户的文件。

目录

一、创建图书类 Book

二、创建书架类 BookList

三、创建用户

四、菜单

五、登录

 六、操作

 七、多次操作

八、程序运行分析 

九、具体实现业务

9.1 退出系统

9.2 显示图书

 9.3 查找图书

9.4 新增图书

9.5 借阅图书

9.6 删除图书

十、可扩展


一、创建图书类 Book

        本类是最好创建的,只需要封装好图书所有的属性,并通过IDEA编译器生成其他方法即可。(当前 isBorrow 属性并没有设置 get 和 set 方法,因为 Boolean 默认值是 fals,即图书一开始被定义之后都是没有被借出的,在新增图书对象的时候就不用输入该属性。后续如果需要用上的话就加上。)

package book;

public class Book {
    private String name;        // 书名
    private String author;      // 作者
    private double price;       // 单价
    private String type;        // 类型
    private boolean isBorrow;   // 是否被借出

    public Book(String name, String author, double 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 double getPrice() {
        return price;
    }

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

    public String getType() {
        return type;
    }

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

    public boolean isBorrow() {
        return isBorrow;
    }

    public void setBorrow(boolean borrow) {
        isBorrow = borrow;
    }
  
    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                ", type='" + type + '\'' +
                ", isBorrow=" + isBorrow +
                '}';
    }
}

对于是否借出输出的信息值要么是 true 要么是 false,应该修改为显示“已借出”/“未借出”汉字信息,因此可以用三目运算符修改 toString() 方法:

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

二、创建书架类 BookList

        图书和书架是 a part of 的关系,或者说 书架和图书是 has a 的关系,这里就得运用到组合的知识(在我的文章“面向对象三大特征之一——继承”中有所提及)

和继承类似,组合也是一种表达类之间关系的方式,也是能够达到代码重用的效果。组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字),仅仅是将一个类的实例作为另外一个类的字段

        将图书类实例成数组表示可以存放若干本图书,此处将其容量定为10本,并定义了一个表示有效数据个数的成员变量,用于统计实际存放的图书数量。

        因为先有书才能管理,因此使用构造方法初始化刚开始存放的图书信息。但是为什么要用构造方法?

        因为在实例化书架这个对象的时候就会直接调用构造方法,也就达到了获取已有图书信息的效果。

package book;

public class BookList {
    private Book[] books = new Book[10];
    private int useSize; // 有效的数据个数【实际存放书本的个数】

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

        this.useSize = 3;
    }
}

三、创建用户

        因为管理员和普通用户都是管理系统的用户,他们存在着共性,因此将共性抽取编写一个抽象类。

package user

public abstract class User {
    protected String name;

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

这个类看似代码很简单,但实际上有一些细节需要注意:

1、name 的修饰符,不应该被封装,因为抽象类是为了被继承的,即如果该属性被 private 修饰,那么子类无法对该属性进行操作;如果想给该属性进行适当的限制,用 protected 比 public 更好;

2、添加构造方法是为了子类继承该类之后,对父类的成员进行初始化。 

然后创建管理员和普通用户两个子类。

package user;

public class AdmUser extends User{  //管理员
    public AdmUser(String name) {
        super(name);
    }
}
package user;

public class NormalUser extends User{  // 普通用户
    public NormalUser(String name) {
        super(name);
    }
}

四、菜单

        对于管理员和普通用户来说,管理权限不一样,因此在进入系统之后显示的操作菜单栏应该也不一样。因此需要分别在两个类中定义不同的菜单:

package user;

import java.util.Scanner;

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

    public void 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("***********************");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的操作:");
        int choice = scanner.nextInt();
    }
}
package user;

import java.util.Scanner;

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

    public void menu(){
        System.out.println("******普通用户菜单******");
        System.out.println("      1. 查找图书");
        System.out.println("      2. 借阅图书");
        System.out.println("      3. 归还图书");
        System.out.println("      0. 退出系统");
        System.out.println("*********************");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的操作:");
        int choice = scanner.nextInt();
    }
}

五、登录

        每个人进入管理系统都要登录自己的账号并选择自己的身份,我们简化成只需要输入名字和选择身份即可进入系统。

        定义一个 Main 类作为程序主入口,所有的操作都集中在此类当中。

        这一步骤的重点是如何根据用户输入的值调用正确身份对象的操作。

import user.AdmUser;
import user.NormalUser;
import user.User;

import java.util.Scanner;

public class Main {
    public static User login(){
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的姓名:");
        String n = scanner.nextLine();
        System.out.println("欢迎" + n + "来到图书管理系统");

        System.out.println("请输入您的身份,1、管理员  2、普通用户");
        int choice = scanner.nextInt();
        if (choice == 1){
            return new AdmUser(n);
        }else if (choice == 2){
            return new NormalUser(n);
        }else {
            return null;
        }
    }

    public static void main(String[] args) {
        User user = login();
        assert user != null;  // 用assert断言关键字判空
        user.menu();
    }
}

        用到了向上转型,将 login 方法返回值类型定义为父类,并在 main 方法中用 User 引用接收。此时需要注意的是,要想调用 menu 方法,必须 User 类中有该方法才行,因为已经被向上转型了,不能调用子类中特有的方法,只能调用父类的方法。也就是说需要在前面编写过的父类中添加 menu(),运行时将发生动态绑定。

package user

public abstract class User {
    protected String name;

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

    public abstract void menu(); // 抽象方法,等着被子类初始化
}

 完成上述代码运行之后将得到下面的显示结果:

      

 六、操作

        用户其实是通过查看书架上的位置从而对图书进行间接性操作,因此应该将操作的动作写到 BookList 类上,但是因为不同的用户操作不同,因此不能全部都编写到 BookList 上,否则一旦将其实例化就难以根据对应的操作菜单实现相应的操作。因此应将这些功能分开编写到不同的类当中。

        但是又该如何根据对象的不同调用不同的操作呢?

        此时需要运用到接口的知识,因为即使是不同的操作,属于同一个行为标准的就可以定义为接口。也可以定义为一个抽象类,但是“操作”这个功能只需要一行代码,不需要常规变量或者方法,所以没有必要定义成抽象类。

步骤:新建一个包,命名为 ioperation 或者 operation ,将查找图书、借阅图书、归还图书、新增图书、删除图书、显示图书和退出系统这些功能的所有类都归属于这个包下。

package ioperation;

public interface IOperation {
    void work(BookList bookList); // 因为需要对书架上的书进行操作,所以传参是必要的
}
package ioperation;

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

此处只以新增图书 AddOperation 类为例,其余功能类定义方法与之类似,因此省略。

图书管理系统对图书的操作文件

        到了用户根据菜单提示输入不同的值以调用不同的操作这个步骤,我们需要将用户输入的值作为 menu() 方法的返回值:

package user;

import java.util.Scanner;

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

    public int menu(){  // 修改了这一行
        //System.out.println("欢迎"+ this.name + "来到图书管理系统"); // main方法中传name值到子类,因此可以被直接调用
        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("***********************");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的操作:");
        int choice = scanner.nextInt();
        return choice;   // 返回用户输入的值
    }
}
package user;

import java.util.Scanner;

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

    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("*********************");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的操作:");
        int choice = scanner.nextInt();
        return choice;
    }
}

        那么又如何根据返回值,找到是 哪个对象哪个方法 呢?

1、哪个对象
        User user = login(); 已经实现了根据用户输入的数字调用不同的对象的方法。

2、哪个方法
        前提是确保调用的对象中包含了这些方法,才能被调用。在抽象类中定义接口类数组包含相应的操作。

package user;

import ioperation.IOperation;

public abstract class User {
    protected String name;

    // 接口类数组还没有被初始化,应该交给子类进行初始化
    protected IOperation[] iOperations;

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

    public abstract int menu();
}

        注意在调用子类对象时,构造方法会初始化好对应的操作对象,因此应该将初始化数组的语句写到子类的构造方法里面。

package user;

import ioperation.*;

import java.util.Scanner;

public class AdmUser extends User{
    public AdmUser(String name) {
        super(name);
        this.iOperations= new IOperation[]{
                new ExitOperation(), // 将选择0退出系统放到数组第一个元素,因为其下标就是0
                new FindOperation(),
                new AddOperation(),
                new DelOperation(),
                new ShowOperation(),
        };
    }

    public int menu(){
        //System.out.println("欢迎"+ this.name + "来到图书管理系统"); // main方法中传name值到子类,因此可以接收
        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("***********************");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的操作:");
        int choice = scanner.nextInt();
        return choice;
    }
}

        有一个巧妙的地方就是用户选择0与数组下标0对应,即 ExitOperation 不是根据打印的菜单位置来放的,而是根据数组下标,用户输入0,调用的就是数组下标为0的 ExitOperation 退出操作。

package user;

import ioperation.*;

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("*********************");
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的操作:");
        int choice = scanner.nextInt();
        return choice;
    }
}

        那么有了操作类的框架,接下是如何通过用户输入的值调用的问题(我们先有框架在最后才具体实现各个操作)。

        可以在用户父类中编写一个调用接口方法的方法,通过 User 类调用 doIOperation 的方法,再通过 doIOperation 方法调用 work 方法。

package user;

import book.BookList;
import ioperation.IOperation;

public abstract class User {
    protected String name;

    // 接口类数组还没有被初始化,应该交给子类进行初始化
    protected IOperation[] iOperations;

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

    public abstract int menu();
 
    public void doIOperation(int choice, BookList bookList){  // 新增这个方法,通过下标访问相应的操作
        iOperations[choice].work(bookList);  // 此处的 iOperations[choice] 相当于 new xxxOperation() 对象,再通过对象调用该对象中的 work() 方法。
    }
}

        为什么要在 User 类中新增 doIOpeatrion 方法?因为在前面编写主程序入口时,我们在 main 方法中用的是 User 引用变量 user 来接收,那么当然调用的也是父类中的方法。

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

import java.util.Scanner;

public class Main {
    public static User login(){
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的姓名:");
        String n = scanner.nextLine();
        System.out.println("欢迎" + n + "来到图书管理系统");

        System.out.println("请输入您的身份,1、管理员  2、普通用户");
        int choice = scanner.nextInt();
        if (choice == 1){
            return new AdmUser(n);
        }else if (choice == 2){
            return new NormalUser(n);
        }else {
            return null;
        }
    }

    public static void main(String[] args) {
        BookList bookList = new BookList();  // 实例化 BookList 对象

        User user = login();

        assert user != null;
        int choice = user.menu();

        user.doIOperation(choice,bookList);  // 调用操作方法并传参
    }
}

 七、多次操作

        上面的程序运行之后我们发现,用户选择1次操作完成之后系统自动退出了。为了能多次进行操作,应该使用循环语句。此处用 while 条件判断语句为 true。

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

import java.util.Scanner;

public class Main {
    public static User login(){
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的姓名:");
        String n = scanner.nextLine();
        System.out.println("欢迎" + n + "来到图书管理系统");

        System.out.println("请输入您的身份,1、管理员  2、普通用户");
        int choice = scanner.nextInt();
        if (choice == 1){
            return new AdmUser(n);
        }else if (choice == 2){
            return new NormalUser(n);
        }else {
            return null;
        }
    }

    public static void main(String[] args) {
        BookList bookList = new BookList();

        User user = login();

        while (true) {
            assert user != null;
            int choice = user.menu();

            user.doIOperation(choice, bookList);
        }
    }
}

八、程序运行分析 

其实整个程序只用4步:

1、实例化书架类(在这一步的同时,初始化好了原有的3本书的信息);

 2、调用 login 静态方法,用户输入的名字作为参数传递,根据用户的选择创建相应的对象并用 User 类型的引用变量进行接收;

3、通过引用变量输出菜单并返回用户选择的操作下标;

4、调用 User 类的操作方法。

九、具体实现业务

我们按照实现的难易程度来补充每个操作的具体实现业务,其顺序如下:

退出系统 -> 显示图书 -> 查找图书 -> 新增图书 -> 借阅图书 -> 归还图书 -> 删除图书

9.1 退出系统

package ioperation;

import book.BookList;

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

        调用系统的方法,因为一般在IDEA中退出程序之后输出的是 Process finished with exit code 0,所以括号里面的数字放的是0。

9.2 显示图书

        显示图书的思路是利用 for 循环遍历书架上存放的图书数组 Book[]:

        1、因为存放的数量不一定是10本,遍历次数也没必要是10本,因此需要取得当前存放图书的有效数量,这就是为什么要定义 useSize 变量的原因;而因为 useSize 被 private 修饰无法直接访问,因此要在 BookList 上设置 set 和 get 方法:

package book;

public class BookList {
    private Book[] books = new Book[10];
    private int useSize; // 有效的数据个数【实际存放书本的个数】

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

        this.useSize = 3;
    }

    public int getUseSize() {
        return useSize;
    }

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

        2、接下来需要获取 books 中的每一本书,因此也需要对成员变量 books 设置 set 和 get 方法,但是我们可以看到由编译器生成的 get 方法返回的是整个数组,与 for 循环要求不符,因此需要修改方法使得每次进入 for 循环只能拿到1本图书(这里的 pos 相当于下标);而 set 设置图书的方法则需要传入 book 对象引用,此方法将在后面的操作中被使用到。

package book;

public class BookList {
    private Book[] books = new Book[10];
    private int useSize; // 有效的数据个数【实际存放书本的个数】

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

        this.useSize = 3;
    }

    public int getUseSize() {
        return useSize;
    }

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

    public Book getBook(int pos) {  // 每次进入for循环中只想取得目的下标的一本图书
        return books[pos];
    }

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

        3、做完上面的工作之后,可以通过 bookList 引用变量调用获取当前存放图书数量的方法,并使用 for 循环调用 getBook 方法查找书架上的每一本书并打印出来。【此处需要注意的是 bookList 是引用变量,而不是数组,不能直接对下标进行操作】

package ioperation;

import book.Book;
import book.BookList;

public class ShowOperation implements IOperation{
    @Override
    public void work(BookList bookList) {
        System.out.println("显示图书......");

        int currentSize = bookList.getUseSize();  // 获取当前存放图书的个数
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBook(i);
            System.out.println(book);

            // Book book = bookList[i];  // 错误的,因为bookList不是数组,而是对象引用
        }
    }
}

 9.3 查找图书

        一般是根据书名来查找相应的图书,然后通过标签(放到系统上是下标)对书架进行查找,因此也用到遍历的思想,同时比较书名是否一样要调用什么方法?

package ioperation;

import book.Book;
import book.BookList;

import java.util.Scanner;

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

        int currentSize = bookList.getUseSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBook(i); // 拿到书架上的书
            if (book.getName().equals(name)){ // 核对该书与用户输入的书名是否一样
                System.out.println("找到了这本书:");
                System.out.println(book);
                return; // 找到就返回,work方法结束
            }
        }
        System.out.println("查无此书……");  // 遍历完都没找到,程序就会执行这一行
    }
}

9.4 新增图书

新增图书有以下步骤:
1、判断书架是否已满;
        此处需要获得图书数组的总长度,因此在 BookList 中需要新增 getBooks() 方法。
2、输入新图书的各种成员变量,构造对象;
3、判断此书是否已存在;
4、放到数组的最后一个位置;
5、有效图书数量加1,即 usedSize++。

package ioperation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class AddOperation implements IOperation{
    @Override
    public void work(BookList bookList) {
        System.out.println("新增图书......");
        // 1、判满
        int currentSize = bookList.getUseSize();
        if (currentSize == bookList.getBooks().length){ // 需要获取数组的总长度,因此 BookList 中需要新增 getBooks() 方法
            System.out.println("书架已满,不能新增图书。");
            return;
        }

        // 2、构造对象
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入书名:");
        String name = scanner.nextLine();

        System.out.println("请输入作者:");
        String author = scanner.nextLine();

        System.out.println("请输入单价:");
        double price = scanner.nextDouble();

        System.out.println("请输入类型:");
        String type = scanner.nextLine();

        Book newBook = new Book(name,author,price,type);

        // 3、判断该书是否存在
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBook(i); // 拿到书架上的书
            if (book.getName().equals(name)){ // 核对该书与用户输入的书名是否一样
                System.out.println("这本图书已存在,不可插入!");
                return; // 找到就返回,不再进入循环
            }
        }

        // 4、插入图书
        bookList.setBooks(currentSize, newBook);
        bookList.setUseSize(currentSize+1);
        System.out.println("成功新增图书。");
    }
}

        在测试运行时出现下面的问题:还未输入类型就已经成功添加了,原因是输入单价之后的“回车”被当中成符被下一个 scanner.nextLine() 读取。解决方法有两个:1、交换输入单价和输入类型的顺序;2、在输入单价之后新增读取空字符的语句:scanner.nextLine();

9.5 借阅图书

核心思想就是输入书名并查找是否有这本书,若有就直接修改 isBorrow 的值。 

package ioperation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class BorrowOperation implements IOperation{
    @Override
    public void work(BookList bookList) {
        System.out.println("借阅图书......");

        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入书名:");
        String name = scanner.nextLine();

        int currentSize = bookList.getUseSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBook(i);
            if (book.getName().equals(name)){
                book.setBorrow(true);
                System.out.println("借阅成功");
                return;
            }
        }
        System.out.println("查无此书……");
    }
}

但是!需要注意如果这本书已经被借出,是无法再次被借到的,因此在借阅之前需要判断一下 isBorrow 的值。

package ioperation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class BorrowOperation implements IOperation{
    @Override
    public void work(BookList bookList) {
        System.out.println("借阅图书......");

        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入您要借阅的书名:");
        String name = scanner.nextLine();

        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
            Book book = bookList.getBook(i);

            if (book.getName().equals(name)){
                if (book.isBorrow()){  // 若为 true 则执行这个语句块
                    System.out.println("这本书已经被借出!");
                    return;
                }
                
                book.setBorrow(true);
                System.out.println("借阅成功");
                return;
            }
        }
        System.out.println("查无此书……");
    }
}

9.6 删除图书

核心思想是找到目的下标的对象,用下一个对象覆盖该位置的对象。

package ioperation;

import book.Book;
import book.BookList;

import java.util.Scanner;

public class DelOperation implements IOperation{
    @Override
    public void work(BookList bookList) {
        System.out.println("删除图书......");

        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入您要删除的书名:");
        String name = scanner.nextLine();

        int currentSize = bookList.getUsedSize();

        // 查找该书的位置,将下标赋值给 pos
        int pos = -1;
        int i = 0;
        for (; i < currentSize; i++) {
            Book book = bookList.getBook(i);
            if (book.getName().equals(name)){
                pos = i;
                break;
            }
        }

        // 没有找到要删除的图书,则结束整个操作
        if (i == currentSize){
            System.out.println("没有您要删除的图书!");
            return;
        }

        for (int j = pos; j < currentSize-1; j++) { // j的范围是 pos~currentSize-1 ,是为了访问时防止溢出
            //bookList[j] = bookList[j+1]; // 按照平时的写法是这样没错,但在这里 bookList 并不是数组

            Book book = bookList.getBook(j+1);
            bookList.setBooks(j,book);
        }

        bookList.setBooks(currentSize, null); // 将最后的对象置空,也可以省略这一步,因为下一条语句使得后续操作无法获取这个对象

        bookList.setUsedSize(currentSize-1);
        System.out.println("删除成功!");
    }
}

十、可扩展

本文中的管理系统应用的是非常基础的知识,若后续学习了更多的知识,将有机会从下面几个方向进行拓展:

        1、每次运行程序,上次操作之后的结果,比如新增了的图书没有了。所以想要存储数据,有以下几个思路:
        ①将数据存储到文件当中,即磁盘上。涉及文件的读取(IO流)知识在JavaEE初阶上;
        ②存储到数据库 MySQL 中,也是相当于存储到磁盘里,在每次运行程序读取数据库即可。

        2、后续学完 EE 初阶的知识,也可以将该项目实现为 WEB 端的一个小练习;需要加入网页和加入框架。

        3、也可以扩展一些功能,如
        ①令书按照书名排序
        ②令书按照价格排序
        ③令书按照作者名排序