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

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

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

目录

[一、创建图书类 Book](#一、创建图书类 Book)

[二、创建书架类 BookList](#二、创建书架类 BookList)

三、创建用户

四、菜单

五、登录

六、操作

七、多次操作

八、程序运行分析

九、具体实现业务

[9.1 退出系统](#9.1 退出系统)

[9.2 显示图书](#9.2 显示图书)

[9.3 查找图书](#9.3 查找图书)

[9.4 新增图书](#9.4 新增图书)

[9.5 借阅图书](#9.5 借阅图书)

[9.6 删除图书](#9.6 删除图书)

十、可扩展


一、创建图书类 Book

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

java 复制代码
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() 方法:

java 复制代码
@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本,并定义了一个表示有效数据个数的成员变量,用于统计实际存放的图书数量。

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

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

java 复制代码
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;
    }
}

三、创建用户

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

java 复制代码
package user

public abstract class User {
    protected String name;

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

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

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

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

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

java 复制代码
package user;

public class AdmUser extends User{  //管理员
    public AdmUser(String name) {
        super(name);
    }
}
java 复制代码
package user;

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

四、菜单

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

java 复制代码
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();
    }
}
java 复制代码
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 类作为程序主入口,所有的操作都集中在此类当中。

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

java 复制代码
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(),运行时将发生动态绑定。

java 复制代码
package user

public abstract class User {
    protected String name;

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

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

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

六、操作

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

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

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

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

java 复制代码
package ioperation;

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

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

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

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

java 复制代码
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;   // 返回用户输入的值
    }
}
java 复制代码
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、哪个方法

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

java 复制代码
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();
}

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

java 复制代码
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 退出操作。

java 复制代码
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 方法。

java 复制代码
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 来接收,那么当然调用的也是父类中的方法。

java 复制代码
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。

java 复制代码
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 退出系统

java 复制代码
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 方法:

java 复制代码
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 对象引用,此方法将在后面的操作中被使用到。

java 复制代码
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 是引用变量,而不是数组,不能直接对下标进行操作】

java 复制代码
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 查找图书

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

java 复制代码
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++。

java 复制代码
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 的值。

java 复制代码
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 的值。

java 复制代码
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 删除图书

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

java 复制代码
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、也可以扩展一些功能,如

①令书按照书名排序

②令书按照价格排序

③令书按照作者名排序

相关推荐
77qqqiqi20 分钟前
解决忘记修改配置密码而无法连接nacos的问题
java·数据库·docker·微服务
ALLSectorSorft34 分钟前
相亲小程序用户注册与登录系统模块搭建
java·大数据·服务器·数据库·python
琢磨先生David1 小时前
Java 垃圾回收机制:自动化内存管理的艺术与科学
java
岁忧1 小时前
(nice!!!)(LeetCode 每日一题) 2561. 重排水果 (哈希表 + 贪心)
java·c++·算法·leetcode·go·散列表
我要成为c嘎嘎大王1 小时前
【C++】类和对象(2)
开发语言·c++
阿华的代码王国1 小时前
【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
android·xml·java·前端·后端
码农BookSea1 小时前
自研 DSL 神器:万字拆解 ANTLR 4 核心原理与高级应用
java·后端
lovebugs2 小时前
Java并发编程:深入理解volatile与指令重排
java·后端·面试
慕y2742 小时前
Java学习第九十一部分——OkHttp
java·开发语言·学习