用Java实现一个图书管理系统

文章介绍了作者使用Java编写的一个简单的图书管理系统,包括用户(管理员和普通用户)和图书两个主要对象,通过继承、多态和接口实现了不同权限的用户对图书的不同操作,如添加、删除、借阅和归还。系统还包含一个书架对象用于存储图书,并通过接口实现功能的统一调用。文章强调了项目在学习编程知识整合过程中的价值。
摘要由CSDN通过智能技术生成

图书管理系统

1.前言

为了解决广大用户喜欢在网上看书的问题,本人写了一套图书管理系统,来帮助大家在网络上看书,但是这套系统并不完善,目前还只是一个小程序,等本人的知识积累到一定程度,就可以把这个图书管理系统,写成网页的形式来供大家使用了。

大家先看下面这两个图,来了解一下整个系统功能的使用:

下面这是管理员的权限:

管理员

下面这是普通用户的权限:

普通用户

大家看完这两个图之后,我们具体分析一下,整个项目是怎么实现的。

2.项目剖析

整个项目可以拆分成两个对象,哪两个对象呢?一个就是用户,一个图书,说白了就是对用户的权限加以限制,不同权限的用户,对图书的操作是不同的,比如说管理员他可能就需要,添加,删除一些图书,而普通用户只需要去选择要不要借阅这些图书来看看。下面就是我具体实现的步骤了:

  1. 那我们第一步其实定义一些实体,来把用户还有图书这些东西给抽象出来,Java是面向对象的,你得把对象给我弄出来是吧!那我们的代码就可以这么写:

    创建一个User对象:

    //代码就是想到什么,写什么,现在觉得user就只需要个名字,我就写了个名字放在这里
    public class User {
        protected String name;  
    
        public User(String name) {
            this.name = name;
        }
    }
    

    创建一个Book对象:

    /**
     * 创建一个图书对象,图书应该有书名,作者,价格,类型以及有没有被借出这些属性吧!
     * 你看我都写成了private,证明我不想直接让别人访问,我得给一些接口吧!得让别人访问
     * 到阿!然后就给了个构造方法,还有get和set方法,还有重写toString方法。
     */
    public class Book {
        private String name; //书名
        private String author; //作者
        private int price; // 价格
        private String type; //类型
        private boolean isBorrowed; // 是否被借出
    
        public Book() {
        }
    
        public Book(String name, String author, int price, String type) {
            this.name = name;
            this.author = author;
            this.price = price;
            this.type = type;
        }
    
        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 int getPrice() {
            return price;
        }
    
        public void setPrice(int 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;
        }
    
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", author='" + author + '\'' +
                    ", price=" + price +
                    ", type='" + type + '\'' +
                    ", isBorrowed=" + (isBorrowed ? "借出" : "未借出") +
                    '}';
        }
    }
    
  2. 既然两个实体都被我们建立出来了,那我们就应该得细分一下了,分什么呢?用户有哪些用户呢?管理员算一个吧!普通用户也算一个吧!那就有两个用户了,那我们是不是也得把这两个用户给建立出来,他们既然都属于用户这一块,我刚好是不是可以直接用到继承的知识,让这两个用户继承我的User,就没有必要再定义name属性了,直接用我父类的就好了。

    创建一个AdminUser对象:

    /**
     * 创建了一个AdminUser来继承User,是不是就可以复用User里面的属性了
     */
    public class AdminUser extends User {
    
        //子类创建了构造方法,是不是先要帮父类进行初始化阿!
        public AdminUser(String name) {
            super(name);
        }
    }
    

    创建一个NormalUser对象:

    /**
     * 创建了一个NormalUser来继承User,是不是也可以复用User里面的属性了
     */
    public class NormalUser extends User {
    
        //子类创建了构造方法,是不是先要帮父类进行初始化阿!
        public NormalUser(String name) {
            super(name);
        }
    }
    
  3. 书这个对象已经建立出来了,但是仔细思考,我创建好了一本书,是不是没有地方放呀!那没有地方怎么办?创建一个书架对象,专门用来存放书这个对象,那说到存放,第一个是不是想到就是数组阿!

    创建一个BookList对象:

    import java.util.Arrays;
    
    public class BookList {
    
        //数组的默认大小
        private static final int DEFAULT_SIZE = 10;
        //创建一个book类型的数组,数组默认大小是10
        private Book[] books = new Book[DEFAULT_SIZE];
        //数组用了几个元素
        private int usedSize;
    
        //在构造方法里,默认初始化了三本书,也就是说程序一执行,就会有三本书在里面
        public BookList() {
            books[0] = new Book("三国演义", "罗贯中", 19, "历史");
            books[1] = new Book("水浒传", "施耐庵", 21, "历史");
            books[2] = new Book("红楼梦", "曹雪芹", 31, "历史");
            //把usedSize改为3
            this.usedSize = 3;
        }
    
    
        public Book getBook(int pos) {
            return books[pos];
        }
    
        public void setBooks(Book book) {
            books[usedSize] = book;
        }
    
        public void setBooks(Book book, int pos) {
            books[pos] = book;
        }
    
    
        public int getUsedSize() {
            return usedSize;
        }
    
        public void setUsedSize(int usedSize) {
            this.usedSize = usedSize;
        }
    
        public static int getDefaultSize() {
            return DEFAULT_SIZE;
        }
    
        //如果数组内容满了,还想插入,那就得扩容了,借助copyOf这个方法,就可以进行扩容
        public void dilatation() {
            this.books =  Arrays.copyOf(books, 2 * DEFAULT_SIZE);
        }
    }
    
  4. 到这里实体的部分我们都建立完了,剩下的就是每个用户对书的操作是不一样的,也就是说我们得把那些操作也得给实现实现吧!那我们对书的操作有哪些呢?有添加图书,借阅图书,删除图书,查找图书,归还图书,显示所有图书以及退出系统,一共七个操作,我想的是,如果我定义一个接口,到时候方法的调用者,他传哪个参数,我就调用哪个方法,这样我就实现了接口的统一,并且能实现你传入什么参数,我就调用什么功能,也就是说我要定义一个接口,让这些功能去实现我的接口,最后就能展现出,你传的参数不一样,我就能给你展示出不同的功能。

    定义一个IOpreationBook接口:

    //里面就一个方法,到时候大家都得实现我这个方法,我就可以用多态的知识,来调用不同的方法
    public interface IOpreationBook {
        void work(BookList bookList);
    }
    

    定义一个AddBookOpreation类:

    //添加一本图书
    //实现IOpreationBook接口,并且重写work方法
    public class AddBookOpreation implements IOpreationBook {
    
        @Override
        public void work(BookList bookList) {
    
            //如果数组满了,就进行扩容
            if (bookList.getUsedSize() == BookList.getDefaultSize()) {
                bookList.dilatation();
            }
    
            Scanner scan = new Scanner(System.in);
            System.out.println("添加图书");
            System.out.println("请输入书名:");
            String name = scan.nextLine();
            System.out.println("请输入作者:");
            String author = scan.nextLine();
            System.out.println("请输入类型:");
            String type = scan.nextLine();
            System.out.println("请输入价格:");
            int price = scan.nextInt();
            //如果数组里面已经有这本输入了,这不允许插入
            if (findBook(bookList, name) != null) {
                System.out.println("不允许重复插入");
                return;
            }
            Book book = new Book(name, author, price, type);
            bookList.setBooks(book);
            //插入成功不要忘记将usedSize+1
            bookList.setUsedSize(bookList.getUsedSize() + 1);
            System.out.println("添加成功");
        }
    }
    

    定义一个BorrowBookOpreation类:

    //借阅一本图书
    //实现IOpreationBook接口,并且重写work方法
    public class BorrowBookOpreation implements IOpreationBook {
    
        @Override
        public void work(BookList bookList) {
            System.out.println("借阅图书");
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入要借阅图书的名字:");
            String name = scan.nextLine();
            //借书的前提是要有这本书,所以要去数组里找一找,看看有没有这本书
            Book book = findBook(bookList, name);
            if (book == null) {
                System.out.println("找不到这本书");
                return;
            }
    		
            //找到这本书之后呢?看看有没有被借出去,没有被借出去,就可以借走了,否则借阅失败
            if (book.isBorrowed() == false) {
                book.setBorrowed(true);
                System.out.println("借阅成功");
            } else {
                System.out.println("借阅失败");
            }
        }
    }
    

    定义一个DelBookOpreation类:

    //删除一本图书
    //实现IOpreationBook接口,并且重写work方法
    public class DelBookOpreation implements IOpreationBook {
       
        @Override
        public void work(BookList bookList) {
            Scanner scan = new Scanner(System.in);
            System.out.println("删除一本图书");
            System.out.println("请输入图书的名字:");
            String name = scan.nextLine();
            //删除图书,得先看看有没有这本书,没有则结束方法,有则返回下标的位置
            int pos = findBook(name, bookList);
            if (pos == -1) {
                System.out.println("找不到这本书");
                return;
            }
            //确定下标的位置了,那就让后面的书覆盖掉前面的书,就可以实现删除了
            int currentLen = bookList.getUsedSize();
            for (int i = pos; i < currentLen - 1; i++) {
                Book book = bookList.getBook(i + 1);
                bookList.setBooks(book, i);
            }
            /**
             * 这里为什么要置为null呢?我们仔细分析的话,后面的书虽然把前面的书覆盖了,但是
             * 后面那本书还存在着,引用还是指向着他,所以我们最好的方法就是把他置为null,
             * 这样就不会指向他了。
             */
            bookList.setBooks(null, currentLen - 1);
            //删完之后,别忘记让usedSize-1
            bookList.setUsedSize(currentLen - 1);
            System.out.println("删除成功");
        }
    }
    

    定义一个FindBookOpreation类:

    //查找一本图书
    //实现IOpreationBook接口,并且重写work方法
    public class FindBookOpreation implements IOpreationBook {
    
        @Override
        public void work(BookList bookList) {
            System.out.println("查找一本书");
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入图书的名字:");
            String name = scan.nextLine();
            int currentLen = bookList.getUsedSize();
            /**
             * 这里就是给数组遍历了,名字一样则返回,不一样就接着循环下去,如果循环都走完了,还没有
             * 找到的话,就说明没有这本书。
             */
            for (int i = 0; i < currentLen; i++) {
                /**
                 * 这里别看写的很长,其实就是获取数组里面的一个对象,然后通过对象获取他的名字
                 * 然后进行比较就完了
                 */
                if (bookList.getBook(i).getName().equals(name)) {
                    System.out.println(bookList.getBook(i));
                    return;
                }
            }
            System.out.println("找不到这本书");
        }
    }
    

    定义一个ReturnBookOpreation类:

    //归还一本图书
    //实现IOpreationBook接口,并且重写work方法
    public class ReturnBookOpreation implements IOpreationBook {
        
        @Override
        public void work(BookList bookList) {
            System.out.println("归还图书");
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入图书的名字:");
            String name = scan.nextLine();
            //归还图书也是要先判断有没有这本书
            Book book = findBook(bookList, name);
            if (book == null) {
                System.out.println("找不到这本书");
                return;
            }
    		
            //有这本书再判断是否已经被借走了,被借走了再归还就没毛病
            if (book.isBorrowed() == true) {
                book.setBorrowed(false);
                System.out.println("归还成功");
            } else {
                System.out.println("归还失败");
            }
        }
    }
    

    定义一个ShowBookOpreation类:

    //展示所有图书
    //实现IOpreationBook接口,并且重写work方法
    public class ShowBookOpreation implements IOpreationBook {
        
        @Override
        public void work(BookList bookList) {
            System.out.println("显示所有图书");
            int currentLen = bookList.getUsedSize();
    		//这里一个for循环就能显示出所有图书
            for (int i = 0; i < currentLen; i++) {
                System.out.println(bookList.getBook(i));
            }
        }
    }
    

    定义一个ExitBookOpreation类:

    //退出系统
    //实现IOpreationBook接口,并且重写work方法
    public class ExitBookOpreation implements IOpreationBook {
        
        @Override
        public void work(BookList bookList) {
            System.out.println("退出系统成功");
            /**
             * 在java里面,正常退出的结束码是0,所以我们只要调用exit这个方法,并赋值一个0,
             * 就可以退出这个程序
             */
            System.exit(0);
        }
    }
    
  5. 到这里对书的操作我们就已经全部实现好了,现在就剩下一个问题了,不同权限的用户,该怎么调用不同的方法呢?我想到的方法是对User这个对象,添加一些东西,添加什么呢?添加一个接口数组,再添加一个对接口数组做操作的方法,为什么要这样设计呢?首先我加了个接口数组,那我的子类一定也继承了这个接口数组,那我就可以在子类为父类初始化的时候,往这个接口数组存放东西,比如说管理员有查找图书,添加图书的这几个功能,我就可以在初始化的时候,为这个数组添加对应功能的实例,然后再通过对接口数组操作的方法,去调用我想用的那几个功能就可以了,说的很抽象,我们具体看看代码就明白了。

    为User对象添加属性和方法:

    public class User {
        protected String name;
        //添加了一个接口数组
        protected IOpreationBook[] iOpreationBook;
    
        public User(String name) {
            this.name = name;
        }
    	
        //添加了一个对接口数组操作的方法
        public void doWork(int choice, BookList bookList) {
            //里面调用了接口数组里的对象里面的方法
            this.iOpreationBook[choice].work(bookList);
        }
    }
    

    让AdminUser这个对象为父类的属性进行初始化:

    public class AdminUser extends User {
    
        public AdminUser(String name) {
            //初始化了父类的name属性
            super(name);
            //初始化了父类里的接口数组,给这个数组里面添加对应的操作
            /**
             * 看到这里,有些同学就会问,为什么你可以这么赋值呢?你们忘记这些操作都实现了
             * IOpreationBook这个接口了吗?那这个接口数组不就是IOpreationBook这个类型吗?
             * 那把我这些操作放进去,不就刚好构成多态,然后发生向上转型吗?不就可以通过父类
             * 来调用我子类重写的方法吗?所以大家还是要把知识点记牢阿!
             */
            this.iOpreationBook = new IOpreationBook[]{
                    new ExitBookOpreation(),
                    new FindBookOpreation(),
                    new AddBookOpreation(),
                    new DelBookOpreation(),
                    new ShowBookOpreation()
            };
        }
    }
    

    让NormalUser这个对象为父类的属性进行初始化:

    public class NormalUser extends User {
    
        public NormalUser(String name) {
            //初始化了父类的name属性
            super(name);
            //初始化了父类里的接口数组,给这个数组里面添加对应的操作
            this.iOpreationBook = new IOpreationBook[]{
                    new ExitBookOpreation(),
                    new FindBookOpreation(),
                    new BorrowBookOpreation(),
                    new ReturnBookOpreation()
            };
        }
    }
    
  6. 看到这里,我们已经把整体的构架搭建完了,写到这里的时候,发现少了点什么,好像没有写菜单阿!那我们把User改为抽象类,然后添加一个抽象方法叫做menu(),然后让子类去重写这个方法,菜单不就出来了吗?

    把User变为抽象类,并添加一个抽象方法

    //我把User改为抽象类,因为这样才能添加抽象方法,我的子类才能重写我的抽象方法
    public abstract class User {
        protected String name;
        protected IOpreationBook[] iOpreationBook;
    
        public User(String name) {
            this.name = name;
        }
    
        //添加抽象方法
        //为什么这个菜单还有返回值呢?我得知道用户输入了点什么呀!我才好去调用对应的方法呀!
        public abstract int menu();
    
        public void doWork(int choice, BookList bookList) {
            this.iOpreationBook[choice].work(bookList);
        }
    }
    

    让AdminUser去实现父类的抽象方法:

    public class AdminUser extends User {
    
        public AdminUser(String name) {
            super(name);
            this.iOpreationBook = new IOpreationBook[]{
                    new ExitBookOpreation(),
                    new FindBookOpreation(),
                    new AddBookOpreation(),
                    new DelBookOpreation(),
                    new ShowBookOpreation()
            };
        }
    
        //实现抽象方法,定义自己的菜单
        @Override
        public int menu() {
            System.out.println("*****************************");
            System.out.println("hello " + 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 scan = new Scanner(System.in);
            int choice = scan.nextInt();
            return choice;
        }
    }
    

    让NormalUser去实现父类的抽象方法:

    public class NormalUser extends User {
    
        public NormalUser(String name) {
            super(name);
            this.iOpreationBook = new IOpreationBook[]{
                    new ExitBookOpreation(),
                    new FindBookOpreation(),
                    new BorrowBookOpreation(),
                    new ReturnBookOpreation()
            };
        }
    
        //实现抽象方法,定义自己的菜单
        @Override
        public int menu() {
            System.out.println("*****************************");
            System.out.println("hello " + name + "欢迎来到图书小练习!");
            System.out.println("1.查找图书");
            System.out.println("2.借阅图书");
            System.out.println("3.归还图书");
            System.out.println("0.退出系统");
            System.out.println("*****************************");
            System.out.println("请输入你的操作:");
            Scanner scan = new Scanner(System.in);
            int choice = scan.nextInt();
            return choice;
        }
    }
    
  7. 到这里我们整体项目就写完了,剩下就是运行我们刚刚写的方法了。

    public class Main {
    
        //写一个登录方法,来获取当前用户是哪个身份
        public static User Login() {
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入你的姓名:");
            String name = scan.nextLine();
            System.out.println("请输入你的身份:1-》管理员,0-》普通用户");
            int choice = scan.nextInt();
    
            if (choice == 1) {
                return new AdminUser(name);
            } else {
                return new NormalUser(name);
            }
        }
    
        public static void main(String[] args) {
            //知道是哪个身份之后,调用那个用户对应的方法
            User user = Login();
            BookList bookList = new BookList();
            while (true) {
                /**
                 * 通过用户调用menu方法,就能知道用户输入了那个数字,然后去调用对应数字的那个方法
                 * 有人说用户乱输入怎么办?没关系阿!我这个是死循环,你不输入0,这个程序就结束不了
                 * 你乱输入,找不到对应方法,写的好一点的,就提示你输入错误,让你重新输入,写的不好
                 * 的,程序就直接报错了,所以呢?不要乱输入哈!
                 */
                int choice = user.menu();
                user.doWork(choice, bookList);
            }
        }
    }
    
  8. 一起看看整体的效果吧!

    aaa

3.总结

当项目写出来的那一刻,挺开心的,虽然这个项目很小,也有很多不足的地方,但是这些都是我们学习的动力,就是在不断受挫,再不断爬起来,那个时候你会发现,你比昨天的自己更棒了。说说这个项目,这个项目用了哪些语法呢?用了变量+各种数据类型+if+for+数组+方法+继承+封装+多态+抽象类+接口,就是把前面所学的知识,进行了个整合,自己把这个系统写完了,对你的帮助,你的理解真的很大,也希望各位同学能继续坚持下去,继续在编程的这条路越走越远。我会把这个项目放在gitee上,如果想看的话,可以去我的主页看看,地址放到结尾了,感谢各位的收看。

4.致谢

gitee的地址:https://gitee.com/smnhaaa/java-project/tree/master/TestBook

感谢你一路看到这里,如果你觉得我写的不错的话,请给我一键三连!!!😉😉

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值