Java 小例子:图书馆课程设计(Java 8 版)

2023-11-12

用 Java 模拟一个图书馆。包括创建图书、创建读者、借书、还书、列出所有图书、列出所有读者、列出已借出的图书、列出过期未还的图书等功能。每个读者最多只能借 3 本书,每个书最多只能借 3 个星期,超过就算过期。

这个例子跟 http://blog.csdn.net/yidinghe/article/details/3940437 相比,增加了 Java 8 特有的语法,包括:Lambda 表达式,java.time 日期 API,streaming API 等。功能也比前者稍微完善了些,体积有所减小。

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 图书馆例子 Java 8 版本
 * created at 2014/11/4
 *
 * @author Yiding
 */
public class LibraryManager {

    public static final Scanner SCANNER = new Scanner(System.in);

    public static final String NUMBERS_ONLY = "^\\d+$"; // 表示只允许输入数字

    public static final String ANY_CONTENT = "^\\S+$";  // 表示可以输入任何内容

    public static void main(String[] args) {
        new LibraryManager().start();
    }

    

    private Library library = new Library();

    private ArrayList<Command> commands = new ArrayList<>();

    private String mainMenu;

    /**
     * 构造方法
     */
    public LibraryManager() {
        initCommands();
        initStudents();
        initBooks();
    }

    private void initBooks() {
        this.library.addBook("论程序员的自我修养", "带鱼", BookType.科学类.toString());
        this.library.addBook("印度四大名著全集", "阿达木", BookType.文学类.toString());
        this.library.addBook("睡眠的好处", "程序员阿迪", BookType.科学类.toString());
        this.library.addBook("架构师2014年10月刊", "美丽女人网", BookType.杂志.toString());
    }

    private void initStudents() {
        this.library.students.add(new Student("张三"));
        this.library.students.add(new Student("李四"));
        this.library.students.add(new Student("王五"));
    }

    /**
     * 初始化命令和主菜单
     */
    private void initCommands() {
        addCommand(new ListCommand<>("所有图书:", () -> library.books), "查询所有图书");
        addCommand(new ListCommand<>("所有学生:", () -> library.students), "查询所有学生");
        addCommand(new AddBookCommand(), "添加图书");
        addCommand(new DeleteBookCommand(), "删除图书");
        addCommand(new BorrowBookCommand(), "借阅图书");
        addCommand(new ReturnBookCommand(), "归还图书");
        addCommand(new ListCommand<>("所有借阅过期的图书:", library::expiredBooks), "查询借阅过期的图书");
        addCommand(new ExitCommand(), "退出");

        this.mainMenu = toMenu("请输入命令", this.commands);
    }

    private void addCommand(Command command, String title) {
        command.title = title;
        this.commands.add(command);
    }

    /**
     * 开始执行交互
     */
    private void start() {
        CommandResult result;

        // 在 while 条件中判断命令的执行结果是否表示要退出程序
        // 只有 ExitCommand 的执行结果是 CommandResult.EXIT
        do {

            try {
                String command = prompt(mainMenu, NUMBERS_ONLY); // 选择命令
                result = executeCommand(command);                // 执行命令
            } catch (CommandCancelException e) {
                result = CommandResult.FAIL;
            }

            System.out.println(result.prompt + "\n");
        } while (result != CommandResult.EXIT);
    }

    /**
     * 打印一条提示消息并返回用户的输入
     *
     * @param prompt  提示消息
     * @param pattern 指定格式,如果用户的输入不符合格式则会反复提示重新输入。为空则不检查用户输入
     *
     * @return 用户的输入
     */
    private String prompt(String prompt, String pattern) {
        String userInput;

        // 在 while 条件中判断用户输入的内容是否符合 pattern 指定的格式
        // 如果 pattern 为 null 则不做判断
        do {
            System.out.print(prompt);
            userInput = SCANNER.nextLine();

            // 用户直接回车时,表示取消命令执行
            if (userInput.equals("")) {
                throw new CommandCancelException();
            }

        } while (pattern != null && !userInput.matches(pattern));

        return userInput;
    }

    // 打印一组选项并返回用户选择的选项内容
    private String prompt(String prompt, List<?> options) {
        int index = promptIndex(prompt, options);
        return options.get(index - 1).toString();
    }

    // 打印一组选项并返回用户选择的位置
    private int promptIndex(String prompt, List<?> options) {
        String menu = toMenu(prompt, options);
        int index;

        do {
            index = Integer.parseInt(prompt(menu, NUMBERS_ONLY));
        } while (index == 0 || index > options.size());

        return index;
    }

    /**
     * 生成菜单内容
     *
     * @param prompt  提示,在列出所有选项后打印出来
     * @param options 选项
     *
     * @return 主菜单内容
     */
    private <T> String toMenu(String prompt, List<T> options) {
        final ArrayList<String> lines = new ArrayList<>();
        final AtomicInteger counter = new AtomicInteger();

        options.forEach((t) -> {
            int index = counter.incrementAndGet();
            String line = index + ": " + t.toString();
            lines.add(line);
        });

        return String.join("\n", lines) + "\n" + prompt + "(1-" + lines.size() + "):";
    }

    /**
     * 执行用户命令
     *
     * @param command 用户命令序号
     *
     * @return 执行结果
     */
    private CommandResult executeCommand(String command) {
        int index = Integer.parseInt(command);

        if (index > 0 && index <= commands.size()) {
            return commands.get(index - 1).execute();
        } else {
            return CommandResult.OK;
        }
    }

    

    static enum CommandResult {
        OK("命令已完成。"), FAIL("命令已取消。"), EXIT("");

        public String prompt;   // 在每个命令结束时打印出来

        CommandResult(String prompt) {
            this.prompt = prompt;
        }

    }

    static enum BookType {文学类, 科学类, 杂志}

    // 表示用户取消命令的异常
    static class CommandCancelException extends RuntimeException {

    }

    static class Book {

        public static final int EXPIRE_BORROW_DAYS = 21;

        public String name;

        public String author;

        public String type;

        public String borrowedBy;

        public String borrowDate;

        Book(String name, String author, String type) {
            this.name = name;
            this.author = author;
            this.type = type;
        }

        public boolean isBorrowed() {
            return this.borrowedBy != null;
        }

        @Override
        public String toString() {
            return name + ",作者:" + author + "," + type +
                    (isBorrowed() ? " -- 已被'" + borrowedBy + "'于" + borrowDate + "借出" : "");
        }

        public boolean isExpired() {
            if (!isBorrowed()) {
                return false;
            }

            // 从当前时间反推过期的借阅时间。如果实际借阅时间在过期的借阅时间之前,则表示过期了
            LocalDate maxBorrowDate = LocalDate.now().minus(EXPIRE_BORROW_DAYS, ChronoUnit.DAYS);
            String maxBorrowDateStr = DateTimeFormatter.ofPattern("yyyyMMdd").format(maxBorrowDate);
            return this.borrowDate.compareTo(maxBorrowDateStr) < 0;
        }
    }

    static class Student {

        public static final int MAX_BORROW = 3;

        public String name;

        Student(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    

    class Library {

        private List<Book> books = new ArrayList<>();

        private List<Student> students = new ArrayList<>();

        /**
         * 添加书籍
         *
         * @param bookName 书名
         * @param author   作者
         * @param type     类型
         *
         * @return 执行结果
         */
        public CommandResult addBook(String bookName, String author, String type) {
            if (books.stream().anyMatch((b) -> b.name.equals(bookName))) {
                System.out.println("添加失败:书名已存在");
                return CommandResult.FAIL;
            }

            this.books.add(new Book(bookName, author, type));
            return CommandResult.OK;
        }

        public List<Book> availableBooks() {
            return this.books.stream().filter((b) -> !b.isBorrowed()).collect(Collectors.toList());
        }

        public List<Book> borrowedBooks() {
            return this.books.stream().filter(Book::isBorrowed).collect(Collectors.toList());
        }

        public List<Book> expiredBooks() {
            return this.books.stream().filter(Book::isExpired).collect(Collectors.toList());
        }

        /**
         * 删除书籍
         *
         * @param index 序号
         *
         * @return 执行结果
         */
        public CommandResult deleteBook(int index) {
            this.books.remove(index);
            return CommandResult.OK;
        }

        public int countBorrowedBooks(String student) {
            return (int) this.books.stream().filter((b) -> student.equals(b.borrowedBy)).count();
        }
    }

    

    /**
     * 表示命令的抽象类
     */
    static abstract class Command {

        public String title;  // 命令标题,将显示在主菜单中

        abstract CommandResult execute();

        @Override
        public String toString() {
            return title;
        }
    }

    // 列出满足要求的对象
    class ListCommand<T> extends Command {

        private Supplier<List<T>> supplier; // 查询满足要求的对象的方法

        private String title;               // 输出标题

        ListCommand(String title, Supplier<List<T>> supplier) {
            this.title = title;
            this.supplier = supplier;
        }

        @Override
        CommandResult execute() {
            System.out.println("\n" + title);
            supplier.get().forEach(System.out::println);
            return CommandResult.OK;
        }
    }

    // 添加图书
    class AddBookCommand extends Command {

        @Override
        CommandResult execute() {
            return library.addBook(
                    prompt("请输入书名:", ANY_CONTENT),
                    prompt("请输入作者:", ANY_CONTENT),
                    prompt("请选择书籍类型:", Arrays.asList(BookType.values()))
            );
        }
    }

    // 删除图书
    class DeleteBookCommand extends Command {

        @Override
        CommandResult execute() {
            if (library.books.isEmpty()) {
                System.out.println("没有可删除的书籍。");
                return CommandResult.FAIL;
            }

            int index = promptIndex("请选择书籍序号", library.books);
            return library.deleteBook(index - 1);
        }
    }

    // 借阅图书
    class BorrowBookCommand extends Command {

        @Override
        CommandResult execute() {
            List<Book> availableBooks = library.availableBooks();
            if (availableBooks.isEmpty()) {
                System.out.println("没有可借阅的图书。");
                return CommandResult.FAIL;
            }

            int index = promptIndex("请选择要借阅的图书", availableBooks);
            Book book = availableBooks.get(index - 1);

            String student = prompt("请选择借阅者:", library.students);
            if (library.countBorrowedBooks(student) >= Student.MAX_BORROW) {
                System.out.println("该同学不能借阅更多图书了。");
                return CommandResult.FAIL;
            }

            String bDate = prompt("请输入借阅日期(YYYYMMDD):", "^\\d{8}$");

            book.borrowedBy = student;
            book.borrowDate = bDate;
            return CommandResult.OK;
        }
    }

    // 归还图书
    class ReturnBookCommand extends Command {

        @Override
        CommandResult execute() {
            List<Book> borrowedBooks = library.borrowedBooks();
            if (borrowedBooks.isEmpty()) {
                System.out.println("没有图书需要归还。");
                return CommandResult.FAIL;
            }

            int index = promptIndex("请选择已借阅的图书", borrowedBooks);
            Book book = borrowedBooks.get(index - 1);
            book.borrowedBy = null;
            book.borrowDate = null;
            System.out.println("图书已归还。");
            return CommandResult.OK;
        }
    }

    // 退出程序
    class ExitCommand extends Command {

        @Override
        CommandResult execute() {
            return CommandResult.EXIT;
        }
    }
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 小例子:图书馆课程设计(Java 8 版) 的相关文章

  • 使用 Intellij Idea 和 gradle 在应用程序引擎上调试 localhost

    我正在使用 IntelliJ 社区添加并使用 Gradle 构建应用程序引擎标准环境应用程序 在迁移到 IntelliJ 和端点框架之前 我使用的是 Android Studio 我无法调试我的本地主机 我添加了 jvmFlags 如下所述
  • 使用 Java 的 Apache Http 摘要身份验证

    我目前正在开发一个 Java 项目 但无法使 http 摘要身份验证正常工作 我尝试使用 Apache 网站 但没有帮助 我有一个需要 HTTP 摘要身份验证的网站 DefaultHttpClient httpclient new Defa
  • Java Logger 未记录到 Netbeans 中的输出

    我正在 Netbeans 中使用 Maven 启动一个 Java 项目 我编写了一些代码来使用 Logger 类进行日志记录 但是 日志记录似乎不起作用 在程序开始时 我运行 Logger getLogger ProjectMainClas
  • eclipse行号状态行贡献项是如何实现的?

    我需要更新状态行编辑器特定的信息 我已经有了自己的实现 但我想看看 eclipse 贡献项是如何实现的 它显示状态行中的行号 列位置 谁能指点一下 哪里可以找到源代码 提前致谢 亚历克斯 G 我一直在研究它 它非常复杂 我不确定我是否了解完
  • 如何调试“com.android.okhttp”

    在android kitkat中 URLConnection的实现已经被OkHttp取代 如何调试呢 OkHttp 位于此目录中 external okhttp android main java com squareup okhttp 当
  • Android中如何使用JNI获取设备ID?

    我想从 c 获取 IMEIJNI 我使用下面的代码 但是遇到了未能获取的错误cls 它总是返回NULL 我检查了环境和上下文 它们都没有问题 为什么我不能得到Context班级 我在网上搜索了一下 有人说我们应该使用java lang Ob
  • 从 MATLAB 调用 Java?

    我想要Matlab程序调用java文件 最好有一个例子 需要考虑三种情况 Java 内置库 也就是说 任何描述的here http docs oracle com javase 6 docs api 这些项目可以直接调用 例如 map ja
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • 如何检查某个元素是否存在于一组项目中?

    In an ifJava中的语句如何检查一个对象是否存在于一组项目中 例如 在这种情况下 我需要验证水果是苹果 橙子还是香蕉 if fruitname in APPLE ORANGES GRAPES Do something 这是一件非常微
  • Java Applet 中的 Apache FOP - 未找到数据的 ImagePreloader

    我正在研究成熟商业产品中的一个问题 简而言之 我们使用 Apache POI 库的一部分来读取 Word DOC 或 DOCX 文件 并将其转换为 XSL FO 以便我们可以进行标记替换 然后 我们使用嵌入到 Java 程序中的 FOP 将
  • Java继承,扩展类如何影响实际类

    我正在查看 Sun 认证学习指南 其中有一段描述了最终修饰符 它说 如果程序员可以自由地扩展我们所知的 String 类文明 它可能会崩溃 他什么意思 如果可以扩展 String 类 我是否不会有一个名为 MyString 的类继承所有 S
  • Jetty、websocket、java.lang.RuntimeException:无法加载平台配置器

    我尝试在 Endpoint 中获取 http 会话 我遵循了这个建议https stackoverflow com a 17994303 https stackoverflow com a 17994303 这就是我这样做的原因 publi
  • 如何将 HTML 链接放入电子邮件正文中?

    我有一个可以发送邮件的应用程序 用 Java 实现 我想在邮件中放置一个 HTML 链接 但该链接显示为普通字母 而不是 HTML 链接 我怎样才能将 HTML 链接放入字符串中 我需要特殊字符吗 太感谢了 Update 大家好你们好 感谢
  • 在 Java 中获取并存储子进程的输出

    我正在做一些需要我开始子处理 命令提示符 并在其上执行一些命令的事情 我需要从子进程获取输出并将其存储在文件或字符串中 这是我到目前为止所做的 但它不起作用 public static void main String args try R
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • hibernate 6.0.2.Final 和 spring boot 2.7.0 的entityManagerFactory bean 未配置问题

    所以最近我想升级我的 Spring Boot 项目项目的一些依赖项 特别是这些组件 雅加达 EE 9 弹簧靴2 7 休眠 6 0 2 Final 完成此操作后 所有更新和代码折射 更新将 javax 导入到 jakarta 以及一些 hib
  • 将 JavaFX FXML 对象分组在一起

    非常具有描述性和信息性的答案将从我这里获得价值 50 声望的赏金 我正在 JavaFX 中开发一个应用程序 对于视图 我使用 FXML
  • 使用 HtmlUnit 定位弹出窗口

    我正在构建一个登录网站并抓取一些数据的程序 登录表单是一个弹出窗口 所以我需要访问这个www betexplorer com网站 在页面的右上角有一个登录链接 写着 登录 我单击该链接 然后出现登录弹出表单 我能够找到顶部的登录链接 但找不
  • 抛出 Java 异常时是否会生成堆栈跟踪?

    这是假设我们不调用 printstacktrace 方法 只是抛出和捕获 我们正在考虑这样做是为了解决一些性能瓶颈 不 堆栈跟踪是在构造异常对象时生成的 而不是在抛出异常对象时生成的 Throwable 构造函数调用 fillInStack
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐

  • 安装和更新node的正确姿势

    干货时刻 本文主要讲解了如何安装node 以及如何更新node的版本 node js 是什么 简称node 是基于Chrome V8引擎的JavaScript JS 运行时环境 node 安装 进入node 官网 点击如下图所示的安装包即可
  • 前端之HTML

    一 概念 1 页面组成 结构 HTML Hyper Text Markup Language 超文本标记语言 页面原始和内容 表现 CSS 网页原始的外观和位置等页面样式 如颜色 大小等 行为 JavaScript 网页模型的定义与交互 简
  • 计算机组成原理慕课测试-第四单元

    计算机字长32位 主存容量为128MB 按字编址 其寻址范围为 0 32M 1 1 B 8b 1 kB 1024 B kB kilobajt 千 1 MB 1024 kB MB megabajt 兆 1 MB 1024 kB MB mega
  • IPV6 阿里DDNS

    IPV6 阿里DDNS 因为需要在家搭建一套环境 并且需要公网能访问 国内的ipv4的地址 各大运营商基本都不会分配ipv4地址 电信宽带好像有地方可以 但是听说很贵 而且是动态的 每过段时间就会改变 发现移动宽带的公网ipv6地址是可以获
  • 微信为什么更受欢迎?

    想必大家都和我一样 曾是一个QQ的忠实用户 认为QQ是最受欢迎的社交软件 其实不然 微信比QQ更受欢迎 只是我们根本不知道微信 所以 我们来谈谈微信NB在那里吧 1 QQ的用户是年轻化 娱乐性强 而微信让不是qq用户的人也加入进来 变得更加
  • CentOS通过nvm安装管理node

    今天搭建CentOS node 环境 原本打算源码安装 环境编译一直出错 为节省时间 直接用nvm 来下载和管理node nvm 是一个开源软件 大家可以在github 上面 下载它的源码https github com creationi
  • 【AI with ML】第 14 章 :在 iOS 应用程序中使用 TensorFlow Lite

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Applications(4)

    CONTENTS Other Applications In this section we cover a few other types of applications of deep learning that are differe
  • java使用smb操作win共享文件夹

    package com zky util import jcifs smb SmbException import jcifs smb SmbFile import jcifs smb SmbFileInputStream import j
  • 新手LearnOpenGL纹理不显示的部分解决方法

    项目场景 在LearnOpenGL学习中遇到的一些问题 照着写但是纹理加载不出来或者两张纹理只加载一张 问题描述 lt 纹理加载不出来 gt 1 文件路径是否正确 是否输出texture load fail等提示 设置了成功检查才会有提示
  • RK3568 Camera 使用

    RK3568 Camera 使用 RK3568 Sensor驱动开发移植 1 RK3568 Sensor驱动开发移植 2 RK3568 Sensor驱动开发移植 3 MIPI CSI用法 RK3568平台仅有一个标准物理mipi csi2
  • 修改pip下载源

    pip国内的一些镜像 阿里云 中国科学技术大学 豆瓣 清华大学 中国科学技术大学 修改源方法一 linux 修改 pip pip conf 如无就创建一个新的 修改内容为 global index url https pypi tuna t
  • 三、C语言进阶:二维指针

    3 二维指针 3 1 什么是二维指针 二维指针与一维指针一样都是保存变量的地址 实例 一维指针存放变量地址 二维指针存放一维指针地址 include
  • Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

    多线程在工作中多多少少会用到 我们知道启动多线程调用的是 start 方法 而不是 run 方法 你知道原因吗 在探讨这个问题之前 我们先来了解一些多线程的基础知识 线程的状态 Java 中 定义了 6 种线程状态 在 Thread 类可以
  • 德标螺纹规格对照表_螺栓螺母德标 欧标 国标对照表

    新德标 旧德标 英文名 中文名 国标 DINENISO4014 DIN931 1 Hexagonheadbolts ProductgradesAandB I SO4014 1999 六角头螺栓 GB T5782 2000 DINENISO4
  • muduo网络库定时器的实现

    一 函数介绍 常见的与时间相关的函数有 sleep alarm usleep nanosleep clock nanosleep gettimer settitimer timer create timer settime timer ge
  • Golang(2)——入门语法之基本语法(函数、变量、类型(自动推导、强转)、流程控制 for、 if else、 switch、defer)

    基本语法 包 函数 var变量 const常量 类型 流程控制 更多类型 包管理 go中没有public private protected等访问控制修饰符 它是通过字母大小表示能否被其他方访问或者调用的 大写的方法就表示是可以被调用的 相
  • C ~ 指针

    指针可以简化一些 C 编程任务的执行 且一些任务 如动态内存分配 没有指针无法执行 所以 学习指针是很有必要的 每个变量都有一个内存位置 每一个内存位置都定义了可使用连字号 运算符访问的地址 它表示了在内存中的一个地址 请看下面的实例 它将
  • 应聘Java笔试时可能出现问题及其答案

    Java基础方面 1 作用域public private protected 以及不写时的区别 答 区别如下 作用域 当前类 同一package 子孙类 其他package public protected friendly private
  • Java 小例子:图书馆课程设计(Java 8 版)

    用 Java 模拟一个图书馆 包括创建图书 创建读者 借书 还书 列出所有图书 列出所有读者 列出已借出的图书 列出过期未还的图书等功能 每个读者最多只能借 3 本书 每个书最多只能借 3 个星期 超过就算过期 这个例子跟 http blo