图书管理系统
使用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;
}
}