文章目录
前言
实现本图书管理系统目的在于将前面“类和对象”、“继承和多态”、“抽象类和接口”这三篇文章进行整理整合,巩固前面所学的知识。
本图书管理系统的重点是:学会对不同模块进行封装,写出不同的类来实现不同的功能。通过本图书管理系统,将前面所学内容都串起来是非常重要的。
本图书管理系统的难点是:理解接口数组,以及能够熟练使用接口数组。
基本情况介绍
这个图书管理系统可以分为管理员登录和用户登录两种登录方式。
- 如果是管理员登录,则要进行身份验证(也就是要先输入密码后才可以进行使用),管理员可进行的操作是:查找图书、新增图书、删除图书、显示图书以及对图书进行指定类型排序。
- 如果是用户登录,则不需要进行身份验证,用户可进行的操作是:查找图书、借阅图书、归还图书以及对图书进行指定类型排序。
主体思想&部分实现代码
我们是要先准备好一些图书,其中我们就要先定义好一个图书类来描述每一本图书的属性(比如:书名、作者、价格、类型以及是否被借出);其次,我们就要定义一个书架来存放这些图书,当然这时候就要先初始化一些书籍来表示在进行操作之前书架上原本就有的图书。
图书类
public class Book implements Comparable<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;
}
//第三部分:对private修饰的成员变量提供对外的接口
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 isBorrowed() {
return isBorrowed;
}
public void setBorrowed(boolean borrowed) {
isBorrowed = borrowed;
}
//第四部分:重写toString方法
@Override
public String toString() {
return "Book{" +
"书名:" + name +
", 作者:" + author +
", 价格:" + price +
", 类型:" + type +
"," + ((isBorrowed==true)?" 已借出":" 未借出") +
'}';
}
//重写compareTo方法(当前使用不到,后面在调用比较器的时候才会使用到)
@Override
public int compareTo(Book o) {
return 0;
}
}
注意:上面代码中重写的compareTo方法暂时用不到,后面会详细讲到。
书架类
public class BookList {
//第一部分:定义书架类的创元变量
private Book[] books = new Book[10]; //这个书架的大小
private int usedSize; //数组中放了几本书
//第二部分:构造方法——初始化书架上的图书
public BookList() {
books[0] = new Book("三国演义","罗贯中",90.00,"小说");
books[1] = new Book("西游记","吴承恩",78.80,"小说");
books[2] = new Book("红楼梦","曹雪芹",89.50,"小说");
this.usedSize = 3;
}
/**
* 获取当前数组当中的元素的个数
* @return
*/
public int getUsedSize() {
return usedSize;
}
/**
* 修改当前数组中元素的个数
* @param usedSize
*/
public void setUsedSize(int usedSize) {
this.usedSize = usedSize;
}
/**
* 获取pos下标的书
* @param pos
* @return
*/
public Book getPos(int pos) {
return books[pos];
}
/**
* 给数组的pos位置 放一本书
* @param pos
* @param book
*/
public void setBooks(int pos,Book book) {
books[pos] = book;
}
}
我们可以定义两个set和get方法分别来传入传出usedSize的值和传入传出某一本图书。
抽象出图书管理系统使用者的类
public abstract class User {
protected String name;
public User(String name) {
this.name = name;
}
//打印操作目录(因为这里目录在普通用户和管理员中显示的效果是不一样的,所以不能够直接写出来,只能够在子类中重写这个抽象方法分别进行实现)
public abstract int menu();
//后面在子类中会实现
public IOperation[] iOperation; //接口数组 这里不进行初始化
public void doOperation(int choice, BookList bookList){
iOperation[choice].work(bookList); //这里是与后面的IOperation接口实现对接的
}
}
子类:管理员
public class AdminUser extends User {
public AdminUser(String name) {
super(name);
//定义一个数组,这里是根据目录中的选项来将不同对象的实现放在这个接口数组中的指定位置上,当使用者输入数字几就会调用下标是多少的对应的实现对象
this.iOperation=new IOperation[]{
new ExitOperation(),
new FindOperation(),
new AddOperation(),
new DelOperation(),
new DisplayOperation(),
new PriceCompareOperation(),
new NameCompareOperation()
};
}
//重写父类中的抽象方法(目录)
public int menu(){
System.out.println("=======管理员用户菜单========");
System.out.println("hello "+this.name+" 欢迎来到图书管理系统");
System.out.println("1.查找图书");
System.out.println("2.新增图书");
System.out.println("3.删除图书");
System.out.println("4.显示图书");
System.out.println("5.按价格排序图书");
System.out.println("6.按书名排序图书");
System.out.println("0.退出系统");
System.out.println("请输入你的操作:>");
Scanner scanner=new Scanner(System.in);
return scanner.nextInt();
}
}
详细请直接看注释即可。
子类:用户
public class NormalUser extends User {
public NormalUser(String name) {
super(name);
//同上,通过下标来调用其中的某一个类
this.iOperation=new IOperation[] {
new ExitOperation(),
new FindOperation(),
new BorrowOperation(),
new ReturnOperation(),
new PriceCompareOperation(),
new NameCompareOperation()
};
}
//重写父类中的抽象方法
public int menu(){
System.out.println("=======普通用户菜单========");
System.out.println("hello "+this.name+" 欢迎来到图书管理系统");
System.out.println("1.查找图书");
System.out.println("2.借阅图书");
System.out.println("3.归还图书");
System.out.println("4.按价格排序图书");
System.out.println("5.按书名排序图书");
System.out.println("0.退出系统");
System.out.println("请输入你的操作:>");
Scanner scanner=new Scanner(System.in);
return scanner.nextInt();
}
}
详细请直接看注释即可。
Main类
有了上面的图书类、书架类、使用者类组成的框架,接下来就可以来写这个最重要的Main类。
import java.util.Scanner;
public class Main {
public static User login(){
System.out.println("请输入姓名:>");
Scanner scanner=new Scanner(System.in);
String name=scanner.nextLine();
while(true){
System.out.println("请输入你的身份:> 1.管理员 0.普通用户");
int choice=scanner.nextInt();
if(choice==1){
System.out.println("请输入密码:>");
Scanner sc=new Scanner(System.in);
String nums=sc.nextLine();
if(nums.equals("123456")) {
System.out.println("登录成功!");
return new AdminUser(name);
}else{
System.out.println("登录失败,输入密码错误!");
}
}else if(choice==0){
return new NormalUser(name);
}else{
System.out.println("选择错误,请重新选择!");
}
}
}
public static void main(String[] args) {
BookList bookList=new BookList(); //准备好书
User user=login(); //向上转型
while(true) {
int choice=user.menu();
user.doOperation(choice,bookList);
}
}
}
- 写一个登录的方法,其中包含输入姓名以及使用者的身份,如果是管理员就要先输入密码;如果只是普通用户,则是直接返回即可
- 这里管理员输入的密码需要判断密码的正确性,如果正确则直接返回即可;如果错误,则需要通过while循环来重新输入密码,如此循环
- 在login方法中,返回的都是User类的子类。管理员返回AdminUser,普通用户返回NormalUser,其中他们都会在返回类中加入用户名字这个参数,这是为什么呢?请继续往下看
- 在代码中可以看到,login方法的返回值是用User类来进行接收的,这就涉及到向上转型的概念了,也就是父类引用引用子类对象,其中从上面父类(也就是User类)代码中可以知道:父类中写的构造方法是有带一个参数的构造方法,这也就解释了第三点中的问题
- 最后就是反复调用子类中的目录以及对图书管理系统的操作了
Operation包
最后就是图书管理系统实现功能的操作了,我们就可以将这些类全部放在Operation这个包里面。
IOperation接口
public interface IOperation {
void work(BookList bookList);
}
首先,我们来定义一个总的接口,因为这个接口在后面实现这些功能类都会使用到。但是其实这个接口要在前面(很早之前实现接口数组那里之前)就已经定义的,因为这个接口其实就相当于一个桥梁。
新增图书类
public class AddOperation implements IOperation {
@Override
public void work(BookList bookList){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入书名:>");
String bookName=scanner.nextLine();
System.out.println("请输入作者:>");
String bookAuthor=scanner.nextLine();
System.out.println("请输入类型:>");
String bookType=scanner.nextLine();
System.out.println("请输入价格:>");
double bookPrice=scanner.nextDouble();
Book book=new Book(bookName,bookAuthor,bookPrice,bookType);
int num=bookList.getUsedSize();
bookList.setBooks(num,book);
bookList.setUsedSize(num+1);
System.out.println("新增图书成功!");
}
}
将这本书的全部信息都输入进去,在书架类中添加进去图书,再将书架中书的数量加1(当然,这里都是要用到set方法来进行修改的),添加完成之后显示新增图书成功。但是,其实这个代码并不完善的,因为当书架上把书都放满的时候,需要对书架进行扩容,此部分内容可再写一个方法来进行判断在扩容(本代码中没有,感兴趣可自己来实现次功能)。
查找图书类
public class FindOperation implements IOperation {
@Override
public void work(BookList bookList){
System.out.println("查找图书!");
Scanner scanner=new Scanner(System.in);
System.out.println("请输入书名:>");
String bookName=scanner.nextLine();
int num=bookList.getUsedSize();
for (int i = 0; i < num; i++) {
Book book=bookList.getPos(i);
if(book.getName().equals(bookName)){
System.out.println(book);
return;
}
}
System.out.println("暂无此书!");
}
}
这个类的实现非常简单,就是输入想要查找的图书名,然后遍历整个书架,来比较是否有图书与输入的图书名一样的。
删除图书类
public class DelOperation implements IOperation {
@Override
public void work(BookList bookList){
int index=0;
Scanner scanner=new Scanner(System.in);
System.out.println("请输入书名:>");
String bookName=scanner.nextLine();
int num=bookList.getUsedSize();
int i=0;
for (i = 0; i < num; i++) {
Book book=bookList.getPos(i);
if(book.getName().equals(bookName)){
index=i;
break;
}
}
if(i==num){
System.out.println("查无此书!");
return;
}
for (int j = index; j < num-1; j++) {
Book book=bookList.getPos(j+1);
bookList.setBooks(j,book);
}
bookList.setBooks(num-1,null);
bookList.setUsedSize(num-1);
System.out.println("删除图书成功!");
}
}
删除图书类时在查找图书类的基础上进行的操作,找到这本书,将排列在这本书后面的图书对它进行覆盖,覆盖完成之后,将数组中的最后一个元素置为空(null),然后将图书的数量减1即可。
展示图书类
public class DisplayOperation implements IOperation {
@Override
public void work(BookList bookList){
int num=bookList.getUsedSize();
for (int i = 0; i < num; i++) {
Book book=bookList.getPos(i);
System.out.println(book);
}
}
}
直接遍历数组来展示所有图书即可(图书类中的toString方法就是在这里用到了)。
退出图书管理系统类
public class ExitOperation implements IOperation {
//销毁或者使用数据后退出
@Override
public void work(BookList bookList){
System.out.println("退出系统!");
int num=bookList.getUsedSize();
for (int i = 0; i < num; i++) {
bookList.setBooks(i,null);
}
System.exit(0);
}
}
先遍历这个图书数组来将全部图书都置为空(null)在退出,这样做的原因是比较安全。
借阅图书类
public class BorrowOperation implements IOperation {
@Override
public void work(BookList bookList){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入书名:>");
String bookName=scanner.nextLine();
int num=bookList.getUsedSize();
for (int i = 0; i < num; i++) {
Book book=bookList.getPos(i);
if(book.getName().equals(bookName)){
if(book.isBorrowed()==false) {
book.setBorrowed(true);
System.out.println("借阅图书成功!");
}else{
System.out.println("此书已被借阅!");
}
return;
}
}
System.out.println("暂无此书!");
}
}
借阅图书类也是在查找图书类的基础上进行添加内容的,先找到这本图书,判断这本书是否已经被借出,如果没被借出,则会显示借阅成功,将这本书置为已借出的状态。
归还图书类
public class ReturnOperation implements IOperation {
@Override
public void work(BookList bookList){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入书名:>");
String bookName=scanner.nextLine();
int num=bookList.getUsedSize();
for (int i = 0; i < num; i++) {
Book book = bookList.getPos(i);
if (book.getName().equals(bookName)) {
book.setBorrowed(false);
System.out.println("归还图书成功!");
return;
}
}
System.out.println("此书不属于此图书馆书籍!");
}
}
这个类也是在查找图书类的基础上进行的操作,查找到这本书后,将其置为未借出的状态即可。
排序图书
这时候就要前面说到的重写compareTo方法。因为这里是对对象中的某一个成员变量进行比较的,所以要先写一个比较器,在可以进行比较。这一部分就是之前文章“抽象类和接口”中三个重要接口中的第二个接口,详细可以看这篇文章(这里不再做讲述):抽象类和接口。
价格比较器
public class PriceComparator implements Comparator<Book> {
@Override
public int compare(Book o1, Book o2) {
if((o1.getPrice()-o2.getPrice())>0){
return 1;
}else if((o1.getPrice()-o2.getPrice())==0){
return 0;
}else{
return -1;
}
}
}
价格比较类
public class PriceCompareOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("按价格排序图书成功!");
int num=bookList.getUsedSize();
Book[] books=new Book[num];
for (int i = 0; i < num; i++) {
books[i]=bookList.getPos(i);
}
PriceComparator priceComparator=new PriceComparator();
Arrays.sort(books,priceComparator);
for (Book book : books) {
System.out.println(book);
}
}
}
书名比较器
public class NameComparator implements Comparator<Book> {
@Override
public int compare(Book o1, Book o2) {
return o1.getName().compareTo(o2.getName());
}
}
书名比较类
public class NameCompareOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("按书名排序图书成功!");
int num=bookList.getUsedSize();
Book[] books=new Book[num];
for (int i = 0; i < num; i++) {
books[i]=bookList.getPos(i);
}
NameComparator nameComparator=new NameComparator();
Arrays.sort(books,nameComparator);
for (Book book : books) {
System.out.println(book);
}
}
}
完整代码
本文中的全部代码存放在码云仓库中:蔡欣致的图书管理系统代码仓库。
最后,希望本文对你理解这个图书管理系统有帮助,能让你看代码变得更轻松,有不正确的地方可以在评论区指出,感谢观看。