图书管理系统

使用JavaSe所学的知识编写的一个纯命令行代码:

Main主体执行


package fun.tanc;


import java.io.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {

        //读取的文件
        File bookListFile  = new File("books.txt");

        Scanner scanner = new Scanner(System.in);
    
        int handle = getHandle(scanner); //调用前置操作

        //对图示的操作,代码主逻辑部分
        bookOptionalOperate(handle, scanner, bookListFile);
    
    }



    /**
     *
     * @param handle 用户选择查询录入修改的操作数
     * @param scanner 输入扫描器
     * @param bookListFile 存储对象流的文件
     */
    private static void bookOptionalOperate(int handle, Scanner scanner, File bookListFile) {
        do {
            if (handle == 1) {
                addAndSetBookOperate(scanner, bookListFile);
            } else if (handle == 2) {
                queryAllAndSingleOperate(scanner, bookListFile);
            } else if (handle == 3) {
                break;
            }
            System.out.print("[1]是否继续选择操作?:(y/n)");
        } while (isNotContinue(scanner));
    }


    /**
     * 单个条件的查询操作
     * @param scanner 输入流 输入是否继续和操作数
     * @param bookListFile 存储对象流的文件
     */
    private static void queryAllAndSingleOperate(Scanner scanner, File bookListFile) {
        do {
            //查询的前置操作,获取用户输入的查询操作数(是ID还是Name还是Price)
            int queryData = queryBooksBeforeOperate(scanner);
            //获取查询操作,依靠输入的操作数,查询的主体方法
            queryBooksMain(scanner, bookListFile, queryData);
            //是否继续操作
            System.out.print("[2]是否继续选择查询操作?(y/n):");
        } while (isNotContinue(scanner));
    }


    /**
     * 判断用户是否选择继续进行操作。
     * 该函数会读取用户的输入,如果用户输入的是"y"(不区分大小写),则认为用户选择继续;否则认为用户选择不继续。
     * 注意:此处实现对"yes"的输入不作为继续操作的判断条件。
     * @param scanner 用于读取用户输入的Scanner对象
     * @return 返回一个布尔值,如果用户选择继续,则返回true;否则返回false。
     */
    private static boolean isNotContinue(Scanner scanner) {
        String isContinue = scanner.next(); // 读取用户输入
        // 判断用户输入是否为"y",且不为"Y"或"yes"(大小写敏感)
        return Objects.equals(isContinue, "y")
                && !Objects.equals(isContinue, "Y")
                && !Objects.equals(isContinue, "yes");
    }


    /**
     * 录入和修改操作
     * @param scanner 输入流,用于获取用户输入的操作数
     * @param bookListFile 存储对象流的文件
     */
    private static void addAndSetBookOperate(Scanner scanner, File bookListFile) {
        do {
            int bookHandle = addSetBookHandle(scanner);
            if (bookHandle == 1) {
                //添加图书
                addBooks(scanner, bookListFile);
            } else if (bookHandle == 2) {
                //修改图书
                setBooksDataBeforeOperate(scanner, bookListFile);
            } else if (bookHandle == 3) {
                break;
            }
            //是否继续操作
            System.out.print("[2]是否继续录入和修改操作?:(y/n)");
        } while (isNotContinue(scanner));
    }


    /**
     * 修改图书数据的前置操作
     * @param scanner 输入扫描器
     * @param bookListFile 存储对象流的文件
     */
    private static void setBooksDataBeforeOperate(Scanner scanner, File bookListFile) {
        do {
            //查询数据中是否有输入的数据,调用了getBookNameList() 方法
            LinkedList<Books> isNamePresent = getBookNameList(scanner, bookListFile);
            //判断列表是否为空,为空就表示没有这个图书
            if (isNamePresent.isEmpty()) {
                System.out.println("没有找到这个图书");
            } else {
                //如果存在则可以修改
                setBookData(scanner, bookListFile, isNamePresent);
            }
            System.out.print("[3]是否继续修改操作?:(y/n)");
        } while (isNotContinue(scanner));
    }


    /**
     * 修改图书数据的选项
     * @param scanner 输入流
     * @param bookListFile 存储对象流的文件
     * @param isNamePresent 获取到的对象集合
     */
    private static void setBookData(Scanner scanner, File bookListFile, LinkedList<Books> isNamePresent) {
        System.out.print("""
        ==========================修改===========================================
                需要修改什么信息:
                【1】图书名
                【2】图书单价
                【3】图书ID
                【4】返回上级菜单
                清输入:
                """);
        //用户舒服来判断修改选项
        int bookSetSetting = scanner.nextInt();
        //修改图书名,使用操作数来判断
        if (bookSetSetting == 1) {
            //调用方法修改图书名
            setBookNameData(scanner, bookListFile, isNamePresent,bookSetSetting);
        } else if (bookSetSetting == 3) {
            //调用方法修改图书ID
            setBookIdData(scanner, bookListFile, isNamePresent, bookSetSetting);
        } else if (bookSetSetting == 2) {
            //调用方法用于修改图书单价
            setBookPriceData(scanner, bookListFile, isNamePresent, bookSetSetting);
        } else if (bookSetSetting == 4) {
            //返回上级菜单
            return;
        } else {
            //输入错误
            System.out.print("输入错误");
        }
    }



    /**
     * 修改书的单价
     * @param scanner 输入流,用于输入修改后的名字
     * @param bookListFile 存储对象流的文件
     * @param isNamePresent 传入符合对象的集合,此时列表应该只有一个Books元素
     * @param bookSetSetting 修改的操作数
     */
    private static void setBookPriceData(Scanner scanner, File bookListFile, LinkedList<Books> isNamePresent, int bookSetSetting) {
        LinkedList<Books> booksList = getBooksList(bookListFile);
        System.out.print("将书"+ isNamePresent.get(0).getBookName()+"的单价"+"修改为: ");
        //调用修改工具
        setBooksDataTools(scanner, bookListFile, isNamePresent, booksList, bookSetSetting);
    }



    /**
     * 修改书的ID
     * @param scanner 输入流,用于输入修改后的名字
     * @param bookListFile 存储对象流的文件
     * @param isNamePresent 传入符合对象的集合,此时列表应该只有一个Books元素
     * @param bookSetSetting 修改的操作数
     */
    private static void setBookIdData(Scanner scanner, File bookListFile, LinkedList<Books> isNamePresent, int bookSetSetting) {
        LinkedList<Books> booksList = getBooksList(bookListFile);
        System.out.print("将书"+ isNamePresent.get(0).getBookName()+"的ID"+"修改为: ");
        //调用修改工具
        setBooksDataTools(scanner, bookListFile, isNamePresent, booksList, bookSetSetting);
    }




    /**
     * 修改指定图书名字并写入操作
     * @param scanner 输入流,用于输入修改后的名字
     * @param bookListFile 存储对象流的文件
     * @param isNamePresent 传入符合对象的集合,此时列表应该只有一个Books元素
     * @param bookSetSetting 修改的操作数
     */
    private static void setBookNameData(Scanner scanner, File bookListFile, LinkedList<Books> isNamePresent,int bookSetSetting) {
        LinkedList<Books> booksList = getBooksList(bookListFile);
        System.out.print("将原名:"+isNamePresent.get(0).getBookName()+"修改为: ");
        //调用修改工具
        setBooksDataTools(scanner,bookListFile,isNamePresent,booksList,bookSetSetting);
    }




    /**
     * 修改书籍数据的工具
     * @param scanner 输入流,用于获取输入修改的对象
     * @param bookListFile 存储对象流的文件
     * @param isNamePresent 需要修改的对象列表
     * @param booksList 获取到的对象列表,主要用来替换
     * @param bookSetSetting 获取修改操作数
     */
    private static void setBooksDataTools(Scanner scanner, File bookListFile,
                                          LinkedList<Books> isNamePresent,
                                          LinkedList<Books> booksList,int bookSetSetting) {
        String setAfterBookName = null;
        int setAfterBookId = 0;
        float setAfterBookPrice = 0;
        if(bookSetSetting == 1){ setAfterBookName = scanner.next();}
        else if(bookSetSetting == 3){ setAfterBookId = scanner.nextInt();}
        else if(bookSetSetting == 2){setAfterBookPrice = scanner.nextFloat();}
        Books setBookData = isNamePresent.get(0);
        //重写了equals和hashCode方法,然后使用indexOf获取索引
        //indexOf判断依据就是调用了equals方法
        int bookIndex = booksList.indexOf(setBookData);
        //判断是否包含这个类,contains本质调用的是indexOf
        if(booksList.contains(setBookData)){
            //修改新Books对象的名字
            if(bookSetSetting == 1){  setBookData.setBookName(setAfterBookName);}
            //修改新Books对象的ID
            else if(bookSetSetting == 3){ setBookData.setBookId(setAfterBookId);}
            else if(bookSetSetting == 2){ setBookData.setPrice(setAfterBookPrice);}

            //直接将对象内的对象替换
            booksList.set(bookIndex, setBookData);
            writeObjectBookList(bookListFile, booksList);
            System.out.println("修改成功");
            System.out.println(booksList.get(bookIndex));
        }
    }








    /**
     * 获取已经存储的图书对象
     * @param bookListFile 存储对象流的文件
     * @return 存储的图书对象
     */
    private static LinkedList<Books> getBooksList(File bookListFile) {
        LinkedList<Books> booksList;
        //获取存储的列表对象
        booksList = readObjectBookList(bookListFile);
        return booksList;
    }


    /**
     * 用于修改指定图书信息的前置操作
     * 用户输入图书名字,使用stream过滤,保存至新的List,随后直接return新的list
     *
     * @param scanner 输入流用于输入图书名字
     * @param bookListFile 存放对象流的文件
     * @return 符合条件的图书
     */
    private static LinkedList<Books> getBookNameList(Scanner scanner, File bookListFile) {
        //使用stream过滤掉不是用户输入的图书
        LinkedList<Books> isNamePresent = queryBooksData(bookListFile,scanner,BooksEnum.BOOKNAME);
        //如果新的列表中有名字相同的,使用ID来判断
        if(isNamePresent.size()>1){
            //调用repeat方法来过滤掉重复元素
            isNamePresent = repeatBookName(isNamePresent, scanner);
        }
        System.out.println("图书信息:"+isNamePresent.get(0));
        return isNamePresent;
    }



    /**
     * 输入数据的参数数据,添加到Result类,然后返回这个类
     * @param scanner 输入流,用于输入图书信息
     * @return Result类,用来存储输入的添加图书信息
     */
    private static Result getInputBookAddData(Scanner scanner) {
        //输入录入图书信息
        System.out.print("请输入图书名: ");
        String bookName = scanner.next();
        System.out.print("请输入图书ID: ");
        //刷新输入的缓存
        scanner.nextLine();
        int bookId = scanner.nextInt();
        System.out.print("请输入图书单价: ");
        float bookPrice = scanner.nextFloat();
        return new Result(bookName, bookId, bookPrice);
    }




    /**
     * 从指定文件中读取对象列表。
     * 该方法实际执行文件的读取操作,利用对象输入流(ObjectInputStream)读取存储在文件中的对象。
     *
     * @param bookListFile 指定存储对象流的文件,从中读取对象。
     * @param readBooksList 用于存储读取的对象的链表。该参数可以为空,方法会负责初始化并填充该链表。
     * @return 返回读取并填充后的书籍链表。如果读取成功,返回的链表将包含从文件中读取的书籍对象。
     */
    private static LinkedList<Books> getBooksLinkedList(File bookListFile, LinkedList<Books> readBooksList) {
        try (ObjectInputStream bookInput= new ObjectInputStream(new FileInputStream(bookListFile))) {
            // 尝试从输入流中读取对象,将其赋值给readBooksList
            readBooksList =(LinkedList<Books>) bookInput.readObject();
        } catch (IOException | ClassNotFoundException e) {
            // 处理可能的输入/输出异常或类未找到异常
            e.printStackTrace();
        }
        return readBooksList;
    }


    /**程序的开始,选择前置操作
     *
     * @param scanner 输入流,用于输入对图书操作信息
     * @return 操作数
     */
    private static int getHandle(Scanner scanner) {
        System.out.println("=========================图书管理系统demo01==============================");
        System.out.println("""
                选择你想要的操作:
                【1】 图书录入和修改
                【2】 图书查询
                【3】 退出程序
                """);
        System.out.print("请输入:");
        return scanner.nextInt();
    }





    /**
     *  添加图书操作
     *  使用scanner接收图书管理员输入的对象
     *  使用了Result对象来保存输入的图书信息
     *  在Java中,record 是自Java 14起引入的一个新特性,并在Java 16中成为正式的语言特性。
     *  record 提供了一种更加简洁的方式来定义一个类,主要用于表示不变的数据集合,
     *  即那些其状态一旦创建后就不应改变的类。Record 类型自动地生成了构造方法、getter 方法、equals(), hashCode(),
     *  和 toString() 方法,大大减少了样板代码。
     * @param scanner 输入流,用于输入图书信息,最后一个所传入getInputBookAddData来执行操作
     * @param bookListFile 存储对象流的文件
     */
    private static void addBooks(Scanner scanner, File bookListFile) {
        LinkedList<Books> booksList;
        do {
            Result result = getInputBookAddData(scanner);
            //创建书本对象
            Books book = new Books(result.bookName(), result.bookPrice(), result.bookId());
            //使用对象流
            //先读取判断文件是否为空
            booksList = readObjectBookList(bookListFile);

            //将输入的图书参数添加到列表中
            booksList.add(book);
            //在写入文件,传入写入文件books列表和书本对象
            writeObjectBookList(bookListFile, booksList);
            System.out.println("图书录入成功!");
            System.out.print("【3】是否继续录入图书?【1】是 【2】否");
        } while (scanner.nextInt() != 2);
    }



    /**
     * record 是一个新引入的特性,它提供了一种更加简洁的方式来定义一个类,主要用于不可变数据的持有。
     * record 类型自动地生成了构造器、getter方法、equals()、hashCode()、和 toString() 方法
     * 它会为传入的参数都创建一个静态的方法return传入插件的本身
     *
     * @param bookName
     * @param bookId
     * @param bookPrice
     */
    private record  Result(String bookName, int bookId, float bookPrice) {
    }

    /**
     * 系统开始的前置操作
     * @param scanner 输入流,用于输入对图书操作信息,是录入还是修改
     * @return bookHandle
     */
    private static int addSetBookHandle(Scanner scanner) {
        System.out.print("""
                【1】 录入图书
                【2】修改图书信息
                【3】返回上级菜单
                清输入:
                """);
        System.out.print("清输输入: ");
        return scanner.nextInt();
    }


    /**
     * 写入文本文件内对象
     * 将读取对象的结果传入,结果应该是一个LinkedList
     * 创建Books数据加入到LinkedList表中*
     *
     * @param bookListFile 存储对象流的文件
     * @param booksList 获取到的对象集合
     */
    private static void writeObjectBookList(File bookListFile, LinkedList<Books> booksList) {
        try(ObjectOutputStream bookOutput = new ObjectOutputStream(new FileOutputStream(bookListFile))){
            //写入
            bookOutput.writeObject(booksList);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 读取文本文件内对象,如果文本文件内没有对象则创建
     * 使用length来判断这个文本文件是否为空
     * 如果不为空则读取内部的对象进行相关操作
     * @param bookListFile 存储对象流的文件
     * @return readBooksList
     */
    private  static LinkedList<Books> readObjectBookList(File bookListFile) {
        //先引用一个列表但是没有创建
        LinkedList<Books> readBooksList = null;
        //判读文件是否为空,如果为空则表示是第一次启动
        if (bookListFile.length() == 0){
            //创建存储表
            readBooksList = new LinkedList<>();
        }else {
            //读取文件随后创建列表对象
            readBooksList = getBooksLinkedList(bookListFile, readBooksList);
        }
        return readBooksList;
    }




    /**
     * 查询操作的主体方法,根据用户输入的操作数执行不同的查询。
     * @param scanner 用于接收用户输入的扫描器
     * @param bookListFile 存储书籍对象的文件
     * @param queryData 用户选择的查询操作数,1代表查询所有数据,2代表进行条件查询
     */
    private static void queryBooksMain(Scanner scanner, File bookListFile, int queryData) {
        do {
            // 根据queryData的值执行不同的查询逻辑
            if (queryData == 1) {
                // 执行查询所有书籍数据的操作
                queryBooksAllData(bookListFile);
            } else if (queryData == 2) {
                //执行单项查询操作
                querySingleAllOperate(scanner, bookListFile);
                //退回到上级菜单
            }else if (queryData == 3) {
                break;
            }
            System.out.print("是否继续查询?【y】是 【n】否");
        } while (isNotContinue(scanner));
    }

    /**
     * 单项查询操作
     * @param scanner 输入流,用于接收用户输入的扫描器
     * @param bookListFile 存储书籍对象的文件
     */
    private static void querySingleAllOperate(Scanner scanner, File bookListFile) {
        // 提示用户选择查询条件,并执行相应的查询操作
        int queryOption = querySingleBeforeOperate(scanner);
        do {
            if (queryOption == 1) {
                // 根据书名查询书籍信息
                LinkedList<Books> queryBooksData = queryBooksData(bookListFile, scanner, BooksEnum.BOOKNAME);
                queryBooksData.forEach(System.out::println);
            } else if (queryOption == 2) {
                // 根据价格查询书籍信息
                LinkedList<Books> queryBooksData = queryBooksData(bookListFile, scanner, BooksEnum.PRICE);
                queryBooksData.forEach(System.out::println);
            } else if (queryOption == 3) {
                // 根据书号查询书籍信息
                LinkedList<Books> queryBooksData = queryBooksData(bookListFile, scanner, BooksEnum.BOOKID);
                queryBooksData.forEach(System.out::println);
            } else if (queryOption == 4) {
                break;
            }
            System.out.print("是否继续查询?【y】是 【n】否");
        }while (isNotContinue(scanner));
    }


    /**
     * 查询所有图书
     * @param bookListFile 存储对象流的文件
     */
    private static void queryBooksAllData(File bookListFile) {
        List<Books> booksList;
        booksList = readObjectBookList(bookListFile);
        booksList.forEach(System.out::println);
    }

    /**
     * 使用单个信息来查询图书
     * @param scanner 扫描器
     */
    private static int querySingleBeforeOperate(Scanner scanner) {
        System.out.print("""
                        需要查询什么信息:
                        【1】图书名
                        【2】图书单价
                        【3】图书ID
                        【4】返回上一级菜单
                        清输入:
                        """);
        return scanner.nextInt();
    }


    /**
     *
     * @param scanner 数据流,输入查询操作数
     * @return 用户输入的查询操作数
     */
    private static int queryBooksBeforeOperate(Scanner scanner) {
        System.out.println("""
                请输入你要查询的图书信息:
                【1】 全部图书
                【2】 单个查询
                【3】 返回到上一级菜单
                """);
        return scanner.nextInt();
    }


    /**
     * 根据图书单项信息查询图书
     * @param bookListFile 存储对象流的文件
     * @param scanner 输入流
     * @param bookStatus 枚举类型,用户判断查询图书数据的类型
     * @return 符合条件的图书
     */
    private static LinkedList<Books> queryBooksData(File bookListFile, Scanner scanner, BooksEnum bookStatus) {
        LinkedList<Books> booksList = getBooksList(bookListFile);
        LinkedList<Books> isNamePresent = null;
        int bookDataId;
        String bookDataName;
        float bookDataPrice;
        //通过枚举类型来判断是需要按照哪个来查询数据
        //判断是否是书名
        if (bookStatus == BooksEnum.BOOKNAME) {
            System.out.print("清输入书名:");
            bookDataName = scanner.next();
            String finalBookDataName = bookDataName;
            isNamePresent = booksList.stream()
                    .filter(element -> Objects.equals(element.getBookName(), finalBookDataName))
                    .collect(Collectors.toCollection(LinkedList::new));
            //判断是否是ID
        } else if (bookStatus == BooksEnum.BOOKID) {
            System.out.print("输入搜索书名ID:");
            bookDataId = scanner.nextInt();
            int finalBookDataId = bookDataId;
            isNamePresent = booksList.stream()
                    .filter(element -> Objects.equals(element.getBookId(), finalBookDataId))
                    .collect(Collectors.toCollection(LinkedList::new));
            //判断是否是价格
        }else if (bookStatus == BooksEnum.PRICE) {
            System.out.print("输入单价搜索:");
            bookDataPrice = scanner.nextFloat();
            float finalBookDataPrice = bookDataPrice;
            isNamePresent = booksList.stream()
                    .filter(element -> Objects.equals(element.getPrice(), finalBookDataPrice))
                    .collect(Collectors.toCollection(LinkedList::new));
        }

        return isNamePresent;
    }









    /**
     * 如果列表中有相同的数据
     * 则需要用户输入ID来继续过滤,使得列表中只有一本书
     * @param isNamePresent 接受的是符合条件的图书集合
     * @param scanner 输入流用于输入ID来过滤重复元素
     * @return 过滤掉重复名字的集合
     */
    private static LinkedList<Books> repeatBookName(LinkedList<Books> isNamePresent, Scanner scanner) {
        System.out.println("存在多个同名的书籍:");
        isNamePresent.forEach(o -> {
            String bookName = o.getBookName();
            int bookId = o.getBookId();
            System.out.println("书名: "+bookName+" 图书序列号: "+bookId);
        });
        System.out.print("请输入需要修改图书的序列号: ");
        int setBookId = scanner.nextInt();
        return  isNamePresent.stream()
                .filter(element -> Objects.equals(element.getBookId(),setBookId))
                .collect(Collectors.toCollection(LinkedList::new));
    }



}

Books类

package fun.tanc;

import java.io.Serial;
import java.io.Serializable;
import java.util.Objects;

public class Books implements Serializable { //实现Serializable方法用来序列化
    @Serial
    private static final long serialVersionUID = 10000; //类的版本号

    private String bookName;
    private float price;
    private int bookId;

    private BooksEnum booksEnum;

    public Books(String bookName, float price, int bookId, BooksEnum booksEnum) {
        this.bookName = bookName;
        this.price = price;
        this.bookId = bookId;
        this.booksEnum = booksEnum;
    }

    public Books(String s, float v, int i) {

    }

    public BooksEnum getBooksEnum() {
        return booksEnum;
    }


    public void setBooksEnum(BooksEnum booksEnum) {
        this.booksEnum = booksEnum;
    }

    @Override
    public String toString() {
        return
                "|"+"图书名: "+ bookName + ", 图书价格: " +
                + price + ", 图书ID: "+
                + bookId + "|";
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public float getPrice() {
        return price;
    }

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

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public Books() {
    }

    /**
     * 直接通过IDEA向导生成的重写equals和HashCode方法
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        //如果对象相等就返回
        if (this == o) return true;
        //如果对象为空或者类不一样就放回false
        if (o == null || getClass() != o.getClass()) return false;
        //创建一个books的Books对象
        Books books = (Books) o;
        //如果当前对象和列表中存在的对象进行下列操作还是相等就true否则false
        return Float.compare(price, books.price) == 0 && bookId == books.bookId && Objects.equals(bookName, books.bookName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(bookName, price, bookId);
    }
}

枚举类

package fun.tanc;

/**
 * 主要来使用枚举来表示查询图书的数据类型(Name,ID,PRICE)
 */
public enum BooksEnum {
    BOOKNAME("书名"),
    PRICE("价格"),
    BOOKID("书号");

    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}