【Java】—— 图书管理系统

news2025/1/11 11:10:27

基于往期学习的类和对象、继承、多态、抽象类和接口来完成一个控制台版本的 “图书管理系统”

在控制台界面中实现用户与程序交互

任务目标:

1、系统中能够表示多本图书的信息

2、提供两种用户(普通用户,管理员)

3、普通用户:查看书籍列表,查找图书,借书,还书

4、管理员:查看书籍列表,查找图书,新增图书,删除图书

任务实现:

1、图书类

创建一个包 library 用来表示 图书管理系统 ,再创建 Book 类表示 一本的图书信息

在类里创建需要用到的属性(书名,作者,价格,类型,是否被借出)

package library;

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

    //构造方法
    //当新增图书时,默认是未被借出
    public Book(String name, String author, double price, String type) {
        this.name = name;
        this.author = author;
        this.price = price;
        this.type = type;
        this.isBorrowed = false;
    }
    //提供 get/set 方法
    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 getBorrowed() {
        return isBorrowed;
    }

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

2、图书列表类

创建一个 BookList 类用来管理多个书籍对象

package library;

public class BookList {
    //创建数组来记录多本书的信息
    private Book[] books = new Book[100];
    private int size = 0;//当前数组中有效元素的个数

    //构造方法
    public BookList(){
        //默认添加几本书,方便后续测试
        books[0] = new Book("西游记","吴承恩",100.0,"古典名著");
        books[1] = new Book("高等数学","高斯",90.0,"自然科学");
        books[2] = new Book("福尔摩斯探案集","阿瑟·柯南·道尔",110,"悬疑推理小说");
        size = 3;
    }

    //提供 get/set 方法

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public Book getBook(int index){
        return books[index];
    }

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

3、用户类

因为 普通用户 和 管理员 是从 用户 中根据不同的权限分离出来的,这里就涉及到了 继承 的知识,让 子类 继承 父类 来减少代码量

package library.User;

import library.IOperation;

//用户父类,派生出子类管理员和普通用户
public abstract class User {
    //这里用 protected 修饰 name,可以让子类直接获取到,不用 get/set 方法
    protected String name;

    //当前这个类能够进行哪些操作,就往这个数组中添加对应的对象
    protected IOperation[] operations;

    //构造方法
    public User(String name) {
        this.name = name;
    }

    //管理员和普通用户的权限不一样,菜单面板也有所不同,
    //但是在父类中没有办法确定使用哪个菜单,所以可以写成抽象类,让子类去进行重写
    //通过返回用户输入的序号来决定执行不同的操作,所以使用 int 类
    //写成 抽象方法 后就需要将这个父类写成 抽象类
    public abstract int menu();
}

父类写到这,我们就可以去创建子类

4、普通用户类(NormalUser)

import java.util.Scanner;

public class NormalUser extends User{

    public NormalUser(String name) {
        super(name);
    }

    @Override
    public int menu() {
        //打印普通用户的菜单
        System.out.println("===========================");
        System.out.println("欢迎您" + 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("请输入您的操作:");
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();
        return choice;
    }
}

5、管理员类(Admin)

import java.util.Scanner;

public class NormalUser extends User{

    public NormalUser(String name) {
        super(name);
    }

    @Override
    public int menu() {
        //打印普通用户的菜单
        System.out.println("===========================");
        System.out.println("欢迎您" + 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("请输入您的操作:");
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();
        return choice;
    }
}

写到这里 书 和 用户 的代码还没有进行交互,这就是面向对象程序设计的典型特点,先创建核心的类/对象,把 核心类/对象属性、方法 搞出来,再通过主逻辑把多个 类/对象 串起来。

6、基本操作接口

创建接口是为了保证所有的类都提供 work 方法,保证 work 的参数都是一致的,也方便之后调用

(这里不使用 接口 使用 抽象类 也是可以的,主要是因为当前没有什么实例属性需要子类来继承(前文中的用户,有 name 属性需要被继承,所以所以使用了抽象类),当两种写法都可以时,优先考虑使用接口)

通过 Operation 把 用户 和 书 关联起来

package library.operation;

import library.BookList;

//通过这个接口来表示用户的一种基本操作
public interface IOperation {
    //给出抽象方法
    //此处操作的 work 方法,是针对 “多本书” 来进行操作的
    //后续再创建操作相关的具体的类,实现这个接口,就要进一步实现这里的操作
    void work(BookList bookList);

}

把操作单独提取成类

这些类都要实现自 IOperation 接口,并且重写其中的 work 方法

package library.operation;

import library.BookList;

public class AddOperation implements IOperation{

    @Override
    public void work(BookList bookList) {

    }
}

 由于当前每个操作都实现了 IOperation 接口,就只需要给这两种用户添加一个 IOperation[ ] 的数组保存能够支持的操作有什么就可以了

回到 普通用户 和 管理员 创建好数组,并在数组里写上要实现的操作

普通用户的操作

数组是 普通用户 和 管理员 都需要,所以直接在父类创建即可

    public NormalUser(String name) {
        super(name);
    
        operations = new IOperation[]{
            new ExitOperation(),
            new ListOperation(),
            new FindOperation(), 
            new BorrowOperation(), 
            new ReturnOperation()
        };
    }

注意这里创建的是数组,元素和元素直接用 “,”隔开。

数组创建写在构造方法内,实例化一个用户,都要操作可以调用

管理员的操作

    public Admin(String name) {
        super(name);

        operations = new IOperation[]{
                new ExitOperation(),
                new ListOperation(),
                new FindOperation(),
                new AddOperation(),
                new DelOperation()
        };
    }

7、Main 类

当我们准备好了所有对象(书、用户、操作),就可以通过一个主逻辑将所有内容串起来

7.1 创建书籍管理对象

BookList bookList = new BookList();

7.2 创建用户对象

这里的身份有两个,所以需要写一个方法,让用户自己选择一个身份

//实例化用户对象
User user = login();

//选择身份方法
private static User login(){
    //让用户输入自己的身份
    Scanner scanner = new Scanner(System.in);
    System.out.println("请输入您的姓名:");
    String name = scanner.next();
    System.out.println("请输入你的身份:(1 普通用户,2 管理员)");
    int role = scanner.nextInt();
    if(role == 1){
        return new NormalUser(name);
    } else if (role == 2) {
        return new Admin(name);
    }else {
        System.out.println("输入有误");
        return null;
    }
}

7.3 构建一个主循环

用户可以不断选择要进行的操作

while(true){
    
}

7.4 显示用户对应的菜单

while(true){
    //这里会触发多态,根据实际指向来决定是谁的菜单
    int choice = user.menu();
}

7.5 根据序号,执行对应操作

//给user 提供一个对应的 “执行” 的方法,最终都是落到对应的 IOperation 对象上
user.work(choice,bookList);


//回到 User 类里创建对应方法
public void work(int choice, BookList bookList){
    //这个操作,是通过输入的 choise 值选择数组中对应的那个操作
    //这里每个操作的下标要和菜单上的序号一一对应
    //这里还可以添加一个判断,判断输入的数字是否合法
    if(choice < 0 && choice > operations.length){
        System.out.println("输入的选项非法");
        return;
    }
    operations[choice].work(bookList);
}

当主逻辑写到这里,就可以进行测试了

测试顺利就可以进入最后的 操作实现

8、IOperation 操作实现

8.1 ExitOperation

package library.operation;

import library.BookList;

public class ExitOperation implements IOperation{

    @Override
    public void work(BookList bookList) {
        //只需要结束整个程序
        System.out.println("Goodbye!");
        //这里的参数是程序的退出码,不重要
        System.exit(0);
    }
}

8.2 ListOperation

package library.operation;

import library.Book;
import library.BookList;

public class ListOperation implements IOperation{

    @Override
    public void work(BookList bookList) {
        System.out.println("查看书籍列表");
        for(int i = 0 ; i< bookList.getSize();i++){
            Book book = bookList.getBook(i);
            System.out.println("[" + i + "]" + book);
        }
    }
}

通过 for 循环对我们存储书的数组进行遍历,循环的次数来自父类的 size ,循环里分别指向图书列表每一个,最后进行打印,这里在打印时添加一个下标,方便之后进行利用下标进行删除

我们会看到打印出来的结果是一段哈希码,我们需要再写一个 toString 方法,回到 Book类中

@Override
public String toString() {
    return "[" + name + "," + author + "," + price + "," + type + "," + isBorrowed + "]";
}

这样就把我们的书籍列表打印出来了

8.3 AddOperation

让用户输入新的书籍,并添加到书籍列表中

package library.operation;

import jdk.swing.interop.SwingInterOpUtils;
import library.Book;
import library.BookList;

import java.util.Scanner;

public class AddOperation implements IOperation{

    @Override
    public void work(BookList bookList) {
        System.out.println("新增图书");
        
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个书名:");
        String name = scanner.next();
        System.out.println("请输入作者:");
        String author = scanner.next();
        System.out.println("请输入价格:");
        Double price = scanner.nextDouble();
        System.out.println("请输入类型:");
        String type = scanner.next();

        Book book = new Book(name,author,price,type);
        int size = bookList.getSize();
        bookList.setBooks(size,book);
        bookList.setSize(size + 1);
        System.out.println("新增图书完成");
    }
}

通过 书 的四个属性创建一本新的书,先获取 size 再通过 size 来找到 书 存放的位置,最后让 size+1 表示当前图书的有效位置

可以看到已经成功将新书存入书籍列表中了

8.4 DelOperation

        在写删除之前先来了解一个小知识,当我们进行删除文件时,总是能非常快速的完成,无论文件大小,删除都是一瞬间。因为在硬盘里,当你进行删除时,硬盘会将你所删除的这个文件在的位置声明这里没有东西,实际上你的文件还是存在,只是之后新的数据会覆盖在这个区域上。这也是文件误删后可以通过技术手段进行找回的原因

        在这写删除仍然使用这个概念,当我们删除图书时,只需要将有效图书个数-1就可以,书依然存在数组里但是不会显示出来。

        这里又牵出了一个问题,有效个数-1只会让最后一本图书不显示,当删除的书不是最末尾的书呢,所以这里可以将问题转换,如果要删除其中一本,只要让这本和最末尾的书进行交换即可

package library.operation;

import library.BookList;

import java.util.Scanner;

public class DelOperation implements IOperation{
    @Override
    public void work(BookList bookList) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入要删除的图书序号");
        //创建变量 index 来记录要删除的图书的序号
        int index = scanner.nextInt();

        //判断一下序号是否在范围内
        if(index<0 || index>=bookList.getSize()){
            System.out.println("序号有误");
            return ;
        }
        //判断删除的序号是不是最后一本
        if(index == bookList.getSize() -1){
            //如果是最后一本,就把 Size-1 
            bookList.setSize(bookList.getSize() -1);
        }else {
            //如果不是最后一本,就要把最后一本复制到删除的序号位置
            //        修改图书(要修改的下标,要修改的图书      (最后一本书的下标))
            bookList.setBooks(index,bookList.getBook(bookList.getSize()-1));
            //最后也要让 Size-1
            bookList.setSize(bookList.getSize() -1);
        }
        System.out.println("删除成功");
    }
}

删除前:

进行删除:

删除后:

原来在最后的福尔摩斯探案集就来到原来高等数学的位置,这样就算删除成功了

8.5 BorrowOperation

借阅只需要修改单个属性即可

package library.operation;

import library.Book;
import library.BookList;

import java.util.Scanner;

public class BorrowOperation implements IOperation{

    @Override
    public void work(BookList bookList) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入要借阅的图书序号");
        int index = scanner.nextInt();

        //这里将我们要借阅的图书实例化,才可以修改它的 Borrow
        Book book = bookList.getBook(index);
        //因为Borrwed属性是布尔值,所以不需要去写 == true
        if(book.getBorrowed()){
            System.out.println("该书已被借阅");
        }else {
            //借阅成功后修改属性
            book.setBorrowed(true);
            System.out.println("借阅成功");
        }
    }
}

借阅过程:

借阅结果:

8.6  ReturnOperation

归还图书也差不多,只需要修改一些内容

package library.operation;

import library.Book;
import library.BookList;

import java.util.Scanner;

public class ReturnOperation implements IOperation{

    @Override
    public void work(BookList bookList) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入要归还的图书序号");
        int index = scanner.nextInt();
        
        Book book = bookList.getBook(index);
        if(book.getBorrowed()){
            book.setBorrowed(false);
            System.out.println("归还成功");
        }else {
            System.out.println("该书未被借阅");
        }
    }
}

8.7 FindOperation

这里写简单一些,只通过书名来查找书的序号

package library.operation;

import library.Book;
import library.BookList;

import java.util.Scanner;

public class FindOperation implements IOperation{

    @Override
    public void work(BookList bookList) {
        //输入姓名
        Scanner scanner = new Scanner(System.in);
        String name = scanner.next();
        //对所有图书进行遍历
        for(int i = 0;i< bookList.getSize();i++){
            //对图书进行实例化
            Book book = bookList.getBook(i);
            //判断字符串是否相等需要用到equals
            if(book.getName().equals(name)){
                //相等则打印书籍信息
                System.out.println(book);
            }
        }
        System.out.println("查找结束");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录ubuntu22.04重启以后无法获取IP地址的问题处理方案

现象描述&#xff1a;我的虚拟机网络设置为桥接模式&#xff0c;输入ifconfig只显示127.0.0.1&#xff0c;不能连上外网。&#xff0c;且无法上网&#xff0c;用ifconfig只有如下显示&#xff1a; 1、sudo -i切换为root用户 2、输入dhclient -v 再输入ifconfig就可以看到多了…

异步操作,promise、axios

一、异步操作&#xff08;异步编程&#xff09;、同步操作 异步操作是指在编程中&#xff0c;某个任务的执行不会立即完成&#xff0c;同时不会阻塞后续代码的执行。在异步操作中&#xff0c;程序可以继续运行&#xff0c;并在异步任务完成时得到通知并处理结果。这与同步操作…

第一性原理构造医疗信创域高质量发展路径应用探析

门诊电子病历录入 摘要&#xff1a; 主要介绍了第一性原理在医疗系统开发中的应用及其重要性。阐述了第一性原理的概念及发展历程&#xff0c;并指出其在各个领域的重要性和应用价值。详细分析了第一性原理在医疗系统开发中的具体影响&#xff0c;包括对医院管理和互联网医疗的…

MySQL8下载安装教程

前言 1.个人经验&#xff0c;仅供参考&#xff01;&#xff01;&#xff01; 2.如果之前有下载过MySQL&#xff0c;请检查是否有删除干净&#xff0c;在控制面板删除最好 下载网址&#xff1a;MySQL :: MySQL Community Downloads 下载步骤 进入网址选择要下载的 下一步网…

算法日记 45 day 图论(并查集基础)

并查集解决什么问题 并查集常用来解决连通性问题。 大白话就是当我们需要判断两个元素是否在同一个集合里的时候&#xff0c;我们就要想到用并查集。 原理 既然是查找是否在同一个集合中&#xff0c;那么这个集合是怎么构建的呢&#xff1f;用一维数组来表示一个有向图&…

PTA DS 6-4 带头结点的链队列的基本操作 (C补全函数)

6-4 带头结点的链队列的基本操作 分数 10 全屏浏览 切换布局 作者 黄复贤 单位 菏泽学院 实现链队列的入队列及出队列操作。 函数接口定义&#xff1a; Status QueueInsert(LinkQueue *Q,ElemType e)&#xff1b; Status QueueDelete(LinkQueue *Q,ElemType *e)&#x…

Windows 系统没有网络链接常见原因以及解决方案

在使用 Windows 电脑时&#xff0c;有时会遇到电脑显示已连接网络&#xff0c;但却无法访问 Internet 的情况&#xff0c;这可能是由多种原因导致的。以下简鹿办公将详细介绍一些常见原因及对应的解决方案。 一、网络连接问题 原因 路由器故障&#xff1a;路由器长时间运行可…

lnmp+discuz论坛 附实验:搭建discuz论坛

Inmpdiscuz论坛 Inmp: t: linux操作系统 nr: nginx前端页面 me: mysql数据库 账号密码&#xff0c;等等都是保存在这个数据库里面 p: php——nginx擅长处理的是静态页面&#xff0c;页面登录账户&#xff0c;需要请求到数据库&#xff0c;通过php把动态请求转发到数据库 n…

杨振宁大学物理视频中黄色的字,c#写程序去掉

先看一下效果&#xff1a;&#xff08;还有改进的余地&#xff09; 我的方法是笨方法&#xff0c;也比较刻板。 1&#xff0c;首先想到&#xff0c;把屏幕打印下来。c#提供了这样一个函数&#xff1a; Bitmap bmp new Bitmap(640, 480, PixelFormat.Format32bppArgb); // 创…

Openlayers基础知识回顾(五)

1、GeoJSON数据的加载 GeoJSON是一种基于JSON的地理空间数据交换格式&#xff0c;它定义了几种类型JSON对象以及它们组合在一起的方法&#xff0c;以表示有关地理要素、属性和它们的空间范围的数据 2、GeoJSON转化为ol要素 new ol.format.GeoJSON().readFeatures() 一、canv…

VTK知识学习(21)- 数据的读写

1、前言 对于应用程序而言&#xff0c;都需要处理特定的数据&#xff0c;VTK应用程序也不例外。 VTK应用程序所需的数据可以通过两种途径获取: 第一种是生成模型&#xff0c;然后处理这些模型数据(如由类 vtkCylinderSource 生成的多边形数据); 第二种是从外部存储介质里导…

javaWeb之过滤器(Filter)

目录 前言 过滤器概述 什么是过滤器 过滤器详细 过滤器的生命周期 过滤器的应用 创建一个简单的Filter类步骤 注意&#xff1a;指定拦截路径&#xff0c;我们有两种方式 实例 前言 本篇博客的核心 知道过滤器的整个拦截过程知道如何指定拦截路径知道过滤器的生命周期…

如何增强通信监控系统

随着员工工作方式的改变&#xff0c;创建安全的远程通信通道至关重要。这对于数据安全也很重要。通信监控系统是确保数据和网络安全的当务之急。 如今&#xff0c;员工越来越多地使用电子通信。在理想世界中&#xff0c;这些渠道的范围是明确界定的。在现实世界中&#xff0c;…

「Mac玩转仓颉内测版47」小学奥数篇10 - 数列求和

本篇将通过 Python 和 Cangjie 双语实现数列求和的计算。通过这个题目&#xff0c;学生将学会如何通过公式法和循环法求解等差数列与等比数列的和。 关键词 小学奥数Python Cangjie数列求和 一、题目描述 编写一个程序&#xff0c;计算等差数列和等比数列的和。用户输入首项…

Robot Framework的 跳出 for循环

一. 简介 前面简单学习了一下&#xff0c;robotframework中的 for循环语句&#xff0c;文章如下&#xff1a; Robot Framework的 for循环语句-CSDN博客 本文继续学习 有关 for循环的其他操作&#xff0c;例如跳出 for循环&#xff0c;或者退出某一次循环等操作。 二. Robo…

nonolog起步笔记-4-Server端的两个线程

nonolog起步笔记-4-Server端的两个线程 Server端的两个线程两个线程的角色与各自的职责RuntimeLogger::compressionThreadMain线程 详细学习一下相关的代码第三个线程第一次出现原位置swip buffer Server端的两个线程 如前所述&#xff0c;nanolog的server端&#xff0c;相对而…

️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南20241206

&#x1f6e0;️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南 &#x1f4dd; 引言 随着大语言模型&#xff08;LLM&#xff09;和人工智能的飞速发展&#xff0c;越来越多的开发者尝试在本地环境中部署大模型进行实验。然而&#xff0c;由于资源需求高、网络限制多…

js:事件监听

事件监听 事件监听&#xff1a;让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就调用一个函数做出响应&#xff0c;也称为绑定事件或注册事件 事件&#xff1a;编程系统内发生的动作或发生的事情 比如用户单击一个按钮下拉菜单 添加事件监听 事件监听三要…

C# (WinForms) 使用 iTextSharp 库将图片转换为 PDF

iTextSharp简介 iTextSharp 是一个开源的 .NET 库&#xff0c;主要用于创建和操作 PDF 文档。它是 iText 的 .NET 版本&#xff0c;iText 是一个广泛使用的 Java 库。iTextSharp 继承了 iText 的核心功能并进行了适应 .NET 平台的调整。 iTextSharp 的主要功能包括&#xff1a…

使用 WebRtcStreamer 实现实时视频流播放

WebRtcStreamer 是一个基于 WebRTC 协议的轻量级开源工具&#xff0c;可以在浏览器中直接播放 RTSP 视频流。它利用 WebRTC 的强大功能&#xff0c;提供低延迟的视频流播放体验&#xff0c;非常适合实时监控和其他视频流应用场景。 本文将介绍如何在Vue.js项目中使用 WebRtcSt…