当前位置: 首页 > news >正文

重返JAVA之路——图书管理系统

目录

一、功能介绍 

二、设计模块

三、系统构建

1.book模块

2.operation模块

输入循环和验证

 查找图书并处理借阅状态

未找到图书的处理

查找删除图书功能实现

未找到图书的处理

 图书查找与归还

 work方法实现图书信息输出

3. user模块实现

四、主菜单 


一、功能介绍 

功能介绍:
1.实现一个简单的图书管理功能

2.拥有管理员和普通用户两种模式

3.功能:

管理员:

1>查找图书

2>新增图书

3>删除图书

4>显示图书

5>退出系统

普通用户:

1>查找图书

2>借阅图书

3>归还图书

4>退出系统

二、设计模块

我们采用分层设计的思想,将系统分为不同的模块,每个模块负责不同的功能,提高代码的可维护性和可扩展性。主要模块包括:

  • 书籍管理模块:负责图书信息的存储和管理,如图书的添加、删除、查找等操作。
  • 用户管理模块:区分普通用户和管理员用户,为不同用户提供不同的操作菜单。
  • 操作接口模块:定义各种操作的接口,实现具体的操作逻辑,如查找、借阅、归还等。

 book模块

        --Book.java

        --Booklist.java

operation模块

        -- AddOperation.java

        --BorrowedOperation.java

        --DelOperation.java

        --ExitOperation.java

        --FindOperation.java
        --IOPeration.java

        --ReturnOperation.java

        --ShowOperation.java

 user模块

        --AdminUser.java

        --NormalUser.java

        --User.java

        --Main.java

那么,让我们现在就开始这个图书管理系统的构建吧~

三、系统构建

1.book模块

我们首先定义一个名为 Book 的类,用于表示图书对象。该类包含图书的基本信息,如书名、作者、价格、类型以及借阅状态,同时提供了对这些属性的访问和修改方法,并且重写toString 方法以方便打印图书信息。

我们声明一个 Book 类所属的包为 book。在 Java 里,包用于组织类和接口,避免命名冲突。

package book;

然后我们定义一个公共类:(public 表明该类能被其他包中的类访问)。

public class Book {

我们将图书的名称、作者、价格、类型以及借阅状态放进Book类中,并设为私有成员变量。我们使用private 访问修饰符保证这些变量只能在 Book 类内部被访问和修改。

private String name;//书名
private String author;//作者
private int price;//价格
private String type;//类型
private boolean isBorrowed;//借阅状态

 我们在内部类中,创建构造方法用于接收传入的图书的名称、作者、价格和类型。通过构造方法中的this关键字将传入的值赋给对应的成员变量。

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

我们需要知道图书的名称、作者、价格和类型,并且易于修改。使用访问器方法用于获取成员变量的值,修改器方法用于修改成员变量的值。。由于 参数是私有属性,外部类无法直接访问,所以要借助这两个方法来获取和修改它们的值。

   public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}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 String getType() {return type;}public void setType(String type) {this.type = type;}
  • getPrice():这是一个 getter 方法,其返回类型为 int,它的功能是返回 price 属性的值。由于 price 是私有属性,外部类无法直接访问,所以要借助这个方法来获取它的值。
  • setPrice(int price):这是一个 setter 方法,它接收一个 int 类型的参数 price,并把该参数的值赋给类的 price 属性。this.price 代表当前对象的 price 属性,这样就能避免局部变量 price 和类的属性 price 产生混淆。
  • getName():这是一个 getter 方法,返回类型为 String,用于返回 name 属性的值。
  • setName(String name):这是一个 setter 方法,接收一个 String 类型的参数 name,并将其赋值给类的 name 属性。
  • getAuthor():这是一个 getter 方法,返回类型为 String,用于返回 author 属性的值。
  • setAuthor(String author):这是一个 setter 方法,接收一个 String 类型的参数 author,并将其赋值给类的 author 属性。
  • getType():这是一个 getter 方法,返回类型为 String,用于返回 type 属性的值。
  • setType(String type):这是一个 setter 方法,接收一个 String 类型的参数 type,并将其赋值给类的 type 属性。

我想要访问和修改图书的借阅状态,这个时候就还需定义两个构造方法,isBorrowed 方法返回图书是否被借出的布尔值,setBorrowed 方法用于设置图书的借阅状态。

public boolean isBorrowed() {return isBorrowed;
}public void setBorrowed(boolean borrowed) {isBorrowed = borrowed;
}

为了能更直观地呈现对象的信息,我们通常会在自定义类中重写 toString() 方法。

toString() 方法重写了 java.lang.Object 类中的 toString() 方法。在 Java 里,Object 类是所有类的父类,也就是说,Java 中的每个类都直接或间接地继承自 Object 类。因此,所有的类都“自带"一个toString() 方法.

  @Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +", price=" + price +", type='" + type + '\'' +((isBorrowed == true) ? "  已经被借出":"  未被借出")+/*", isBorrowed=" + isBorrowed +*/'}';}
}

重写之后,当调用 Book 对象的 toString() 方法时,就会返回一个包含 Book 对象详细信息的字符串,从而方便调试、日志记录或者展示对象信息。

 这样我们就完成了Book类的构建,接下来,我们开始构建Booklist类,Booklist 类作为一个图书管理容器,负责存储、初始化和管理图书信息。

我们首先声明 Booklist 类所属的包为 book

package book;

 定义了一个公共类 Booklistpublic 表明该类能被其他包中的类访问。

public class BookList {private Book[] books = new Book[10];private int usedSize;//计数器 来记录 当前实际放的书的书目!public BookList() {//构造方法 来初始化成员this.books[0] = new Book("三国演义","罗贯中",10,"小说");this.books[1] = new Book("西游记","吴承恩",23,"小说");;this.books[2] = new Book("红楼梦","曹雪芹",8,"小说");;this.usedSize = 3;}
  • Books:这是一个 Book 类型的数组,用于存储图书对象。数组的大小被初始化为 10,意味着该图书列表最多可以存储 10 本图书。
  • usedSize:表示当前图书列表中实际存储的图书数量。
  • Booklist 类的构造方法,用于初始化图书列表。在创建 Booklist 对象时,会自动创建三本图书并将它们存储在数组的前三个位置,同时将 usedSize 设置为 3,表示当前图书列表中有 3 本图书。

我们仍然使用访问器和修改器, 获取和修改参数。

//访问和修改图书信息
public Book getBook(int pos) {return Books[pos];
}public void setBook(int pos, Book book) {this.Books[pos] = book;
}public Book[] getBooks() {return Books;
}public void setBooks(Book[] Books) {this.Books = Books;
}// 管理图书数量
public int getUsedSize() {return usedSize;
}public void setUsedSize(int usedSize) {this.usedSize = usedSize;
}
  • getBook(int pos):根据传入的索引位置 pos,返回图书列表中对应位置的 Book 对象。
  • setBook(int pos, Book book):将传入的 Book 对象存储到图书列表的指定位置 pos
  • getBooks():返回整个图书列表数组。
  • setBooks(Book[] Books):将传入的 Book 数组赋值给当前图书列表数组。
  • getUsedSize():返回当前图书列表中实际存储的图书数量。
  • setUsedSize(int usedSize):设置当前图书列表中实际存储的图书数量。

2.operation模块

operation模块中,我们有添加、借阅、删除、退出、查找、归还、显示这些功能。下面我们就为大家逐一讲解。

我们首先需要一个名为 IOPeration 的 Java 接口,为图书管理系统中的各种操作提供一个统一的标准和规范。通过这个接口,不同的操作类(如添加图书、删除图书、查找图书等)可以实现相同的方法,方便在系统中统一调用这些操作。

// IOPeration 
package operation;
import book.Booklist;
import java.util.Scanner;public interface IOPeration {void work(Booklist booklist, Scanner scanner);
}

我们在接口中定义了一个名为 work 的抽象方法。

这个方法接收两个参数:一个是 Booklist 类型的对象 booklist,用于表示图书列表;另一个是 Scanner 类型的对象 scanner,用于从控制台读取用户输入。所有实现 IOPeration 接口的类都必须实现 work 方法。


 下面,我们来创建AddOperation类,用于添加图书的信息。

  • 功能:向 Booklist 中添加一本新图书。
  • 核心逻辑
    • 提示用户输入图书的名称、作者、价格、类型等信息。
    • 创建新的 Book 对象。
    • 将新图书添加到 Booklist 的数组中(需考虑数组是否已满,可能需要动态扩容或提示错误)。
//AddOperation
package operation;import book.Book;
import book.Booklist;
import java.util.Scanner;public class AddOperation implements IOPeration {@Overridepublic void work(Booklist booklist, Scanner scanner) {boolean needInput = true;while (true) {if (needInput) {System.out.println("新增图书!");System.out.println("请输入图书名称:");}String name = scanner.nextLine();if (name == null || name.trim().isEmpty()) {System.out.println("(注意:输入的图书名称不能为空!)");needInput = false; // 此时不需要再提示输入,避免重复continue;}System.out.println("请输入图书作者:");String author = scanner.nextLine();System.out.println("请输入图书价格:");int price = scanner.nextInt();scanner.nextLine(); // 消耗掉换行符System.out.println("请输入图书类型:");String type = scanner.nextLine();Book newBook = new Book(name, author, price, type);int usedSize = booklist.getUsedSize();if (usedSize < booklist.getBooks().length) {booklist.setBook(usedSize, newBook);booklist.setUsedSize(usedSize + 1);System.out.println("图书添加成功!");} else {System.out.println("图书列表已满,无法添加!");}return;}}
}

AddOperation 类实现了 IOPeration 接口,用于添加新的图书。在 work 方法中,通过 Scanner 类从控制台获取用户输入的图书信息,(如书名、作者、价格等),创建一个新的 Book 对象,并将其添加到 booklist (图书列表中)。如果图书列表已满,则提示用户无法添加。


接下来我们创建BorrowedOperation类,用于借阅书籍。

  • 功能:将 Booklist 中的某本图书标记为 “已借出”。
  • 核心逻辑
    • 提示用户输入书名或索引,查找目标图书。
    • 若图书存在且未被借出,将其 isBorrowed 状态设为 true
    • 若图书已被借出或不存在,提示相应错误。
//BorrowedOperation.java
package operation;import book.Book;
import book.Booklist;
import java.util.Scanner;public class BorrowedOperation implements IOPeration {@Overridepublic void work(Booklist booklist, Scanner scanner) {boolean needInput = true;while (true) {if (needInput) {System.out.println("请输入要借阅的图书名称:");}String name = scanner.nextLine();if (name == null || name.trim().isEmpty()) {System.out.println("(请注意:输入的图书名称不能为空!)");needInput = false;continue;}int usedSize = booklist.getUsedSize();for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book.getName().equals(name)) {if (book.isBorrowed()) {System.out.println("该图书已被借出,无法借阅!");} else {book.setBorrowed(true);System.out.println("图书借阅成功!");}return;}}System.out.println("未找到该图书!");return;}}
}

我们来看这段代码: 

输入循环和验证
    boolean needInput = true;
    while (true) {if (needInput) {System.out.println("请输入要借阅的图书名称:");}String name = scanner.nextLine();if (name == null || name.trim().isEmpty()) {System.out.println("(请注意:输入的图书名称不能为空!)");needInput = false;continue;}
    • 我们创建boolean needInput = true;:定义一个布尔变量 needInput,用于控制是否显示输入提示信息。
    • (注意:needInput 变量的主要作用是避免在用户输入为空时重复显示提示信息,以此提升用户体验。不过,该逻辑存在一定局限性,例如当用户连续多次输入空值时,后续就不会再显示输入提示了。)
    • while (true):创建一个无限循环,用于不断提示用户输入图书名称,直到输入有效信息。
    • if (needInput):如果 needInput 为 true,则显示输入提示信息。
    • String name = scanner.nextLine();:从控制台读取用户输入的图书名称。
    • if (name == null || name.trim().isEmpty()):检查用户输入的图书名称是否为 null 或为空字符串(去除前后空格后)。如果是,则显示错误提示信息,将 needInput 设置为 false,并使用 continue 语句跳过本次循环,继续下一次循环。
     查找图书并处理借阅状态
    int usedSize = booklist.getUsedSize();
    for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book.getName().equals(name)) {if (book.isBorrowed()) {System.out.println("该图书已被借出,无法借阅!");} else {book.setBorrowed(true);System.out.println("图书借阅成功!");}return;}
    }
    • int usedSize = booklist.getUsedSize();:获取图书列表中实际存在的图书数量。
    • for (int i = 0; i < usedSize; i++):遍历图书列表中的每一本图书。
    • Book book = booklist.getBook(i);:从图书列表中获取当前索引对应的图书对象。
    • if (book.getName().equals(name)):检查当前图书的名称是否与用户输入的图书名称相同。
      • 如果相同,再检查该图书的借阅状态:
        • 如果 book.isBorrowed() 为 true,表示该图书已被借出,显示相应的提示信息。
        • 如果 book.isBorrowed() 为 false,表示该图书未被借出,将其借阅状态设置为 true,并显示借阅成功的提示信息。
      • 无论图书是否被借出,都使用 return 语句结束 work 方法
    未找到图书的处理
    System.out.println("未找到该图书!");
    return;
    • 如果遍历完整个图书列表都没有找到与用户输入的图书名称相同的图书,则显示未找到图书的提示信息,并使用 return 语句结束 work 方法。

      接下来,我们来创建一个DelOperation类,用于删除图书信息。

    • 功能:从 Booklist 中删除一本图书(按索引或书名)。
    • 核心逻辑
      • 提示用户输入要删除的图书索引或名称。
      • 找到目标图书后,将其从数组中移除(需处理数组元素的前移,避免空指针)。
      • 更新 usedSize(实际图书数量)。
    //DelOperation.java
    package operation;import book.Book;
    import book.Booklist;
    import java.util.Scanner;public class DelOperation implements IOPeration {@Overridepublic void work(Booklist booklist, Scanner scanner) {boolean needInput = true;while (true) {if (needInput) {System.out.println("请输入要删除的图书名称:");}String name = scanner.nextLine();if (name == null || name.trim().isEmpty()) {System.out.println("(注意:输入的图书名称不能为空!)");needInput = false;continue;}int usedSize = booklist.getUsedSize();for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book.getName().equals(name)) {for (int j = i; j < usedSize - 1; j++) {booklist.setBook(j, booklist.getBook(j + 1));}booklist.setUsedSize(usedSize - 1);System.out.println("图书删除成功!");return;}}System.out.println("未找到该图书!");return;}}
    }
    查找删除图书功能实现
    int usedSize = booklist.getUsedSize();
    for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book.getName().equals(name)) {for (int j = i; j < usedSize - 1; j++) {booklist.setBook(j, booklist.getBook(j + 1));}booklist.setUsedSize(usedSize - 1);System.out.println("图书删除成功!");return;}
    }
    • int usedSize = booklist.getUsedSize();:获取图书列表中实际存在的图书数量。
    • for (int i = 0; i < usedSize; i++):遍历图书列表中的每一本图书。
    • Book book = booklist.getBook(i);:从图书列表中获取当前索引对应的图书对象。
    • if (book.getName().equals(name)):检查当前图书的名称是否和用户输入的图书名称一致。
      • 若一致,通过内层的 for 循环将该图书之后的所有图书向前移动一位,覆盖掉要删除的图书。
      • booklist.setUsedSize(usedSize - 1);:将图书列表的实际数量减 1。
      • 显示删除成功的提示信息,并使用 return 语句结束 work 方法。
    未找到图书的处理
    System.out.println("未找到该图书!");
    return;
    • 若遍历完整个图书列表都未找到与用户输入的图书名称相同的图书,就显示未找到图书的提示信息,并使用 return 语句结束 work 方法。

      接下来,我们来创建一个ExitOperation,用于退出系统操作。

    • 功能:结束图书管理系统的运行。
    • 核心逻辑
      • 打印退出提示信息(如 “退出系统”)。
      • 终止程序(通常调用 System.exit(0))。
    //ExitOperation.java
    package operation;import book.Booklist;
    import java.util.Scanner;public class ExitOperation implements IOPeration {@Overridepublic void work(Booklist booklist, Scanner scanner) {System.out.println("退出系统");scanner.close();System.exit(0);}
    }

     接下来,我们来创建一个FindOperation类,用于查找图书信息。

    • 功能:在 Booklist 中查找符合条件的图书(按书名、作者、类型等)。
    • 核心逻辑
      • 提示用户输入查找条件(如书名)。
      • 遍历 Booklist 中的图书,匹配条件并打印结果。
      • 若未找到,提示 “未找到该图书”。
    //FindOperation.java
    package operation;import book.Book;
    import book.Booklist;
    import java.util.Scanner;public class FindOperation implements IOPeration {private static final String BACK_COMMAND = "back"; // 定义返回上一级菜单的指@Overridepublic void work(Booklist booklist, Scanner scanner) {boolean needInput = true;while (true) {if (needInput) {System.out.println("请输入要查找的图书名称(输入 \"" + BACK_COMMAND + "\" 返回上一级菜单):");}String name = scanner.nextLine();if (name.equalsIgnoreCase(BACK_COMMAND)) {System.out.println("已返回上一级菜单。");break;}if (name == null || name.trim().isEmpty()) {System.out.println("(注意:输入的图书名称不能为空!)");needInput = false; // 此时不需要再提示用户输入,避免重复continue;}Book foundBook = findBookByName(booklist, name);if (foundBook != null) {printBookInfo(foundBook);break;} else {System.out.println("未找到该图书!请重新输入要查找的图书名称,或输入 \"" + BACK_COMMAND + "\" 返回上一级菜单:");needInput = true; // 未找到图书,需要重新提示用户输入}}}private Book findBookByName(Booklist booklist, String name) {if (booklist == null) {return null;}int usedSize = booklist.getUsedSize();for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book == null) {continue;}if (book.getName().equals(name)) {return book;}}return null;}private void printBookInfo(Book book) {System.out.println("书名: " + book.getName());System.out.println("作者: " + book.getAuthor());System.out.println("价格: " + book.getPrice());System.out.println("类型: " + book.getType());System.out.println("是否借出: " + (book.isBorrowed()? "是" : "否"));}
    }

     findBookByName 方法 - 图书查找逻辑

    private Book findBookByName(Booklist booklist, String name) {if (booklist == null) {return null;}int usedSize = booklist.getUsedSize();for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book == null) {continue;}if (book.getName().equals(name)) {return book;}}return null;
    }

     FindOperation 类实现 work 方法,通过 booklist 获取图书列表,利用 scanner 接收用户输入的图书名称,然后在图书列表中查找该图书并输出结果。

    • 功能:在 Booklist 中查找指定名称的图书,若找到则返回该图书对象,否则返回 null
    • 分析
      • 对 booklist 进行空值检查,避免空指针异常。
      • 遍历图书列表,对每本图书进行检查,若图书名称与输入名称匹配,则返回该图书。
      • 若遍历完整个列表都未找到匹配图书,则返回 null

    接下来,我们创建一个ReturnOperation.类,用于归还图书 。

    //ReturnOperation.java
    package operation;import book.Book;
    import book.Booklist;
    import java.util.Scanner;public class ReturnOperation implements IOPeration {@Overridepublic void work(Booklist booklist, Scanner scanner) {boolean needInput = true;while (true) {if (needInput) {System.out.println("请输入要归还的图书名称:");}String name = scanner.nextLine();if (name == null || name.trim().isEmpty()) {System.out.println("(注意:输入的图书名称不能为空!)");needInput = false;continue;}int usedSize = booklist.getUsedSize();for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book == null) {continue;}if (book.getName().equals(name)) {if (book.isBorrowed()) {book.setBorrowed(false);System.out.println("图书归还成功!");} else {System.out.println("该图书未被借出,无需归还!");}return;}}System.out.println("未找到该图书!");return;}}
    }
     图书查找与归还
    int usedSize = booklist.getUsedSize();
    for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);if (book == null) {continue;}if (book.getName().equals(name)) {if (book.isBorrowed()) {book.setBorrowed(false);System.out.println("图书归还成功!");} else {System.out.println("该图书未被借出,无需归还!");}return;}
    }
    • 功能:在图书列表中查找用户输入的图书,并根据图书的借阅状态进行相应处理。
    • 分析
      • 首先获取图书列表的实际使用大小 usedSize,然后使用 for 循环遍历图书列表。
      • 若当前图书对象为 null,使用 continue 语句跳过本次循环,继续检查下一本图书。
      • 若找到与用户输入名称匹配的图书,检查其借阅状态:
        • 若图书已被借出(book.isBorrowed() 为 true),将图书的借阅状态设置为 false,表示归还成功,并输出相应提示信息,然后使用 return 语句结束方法。
        • 若图书未被借出,输出 “该图书未被借出,无需归还!” 的提示信息,然后结束方法。

    接下来,我们创建一个ShowOperation类展示所有图书。

    • 功能:遍历 Booklist 并打印所有图书的详细信息。
    • 核心逻辑
      • 遍历 Booklist 的 Books 数组,根据 usedSize 确定有效范围。
      • 调用每本图书的 toString() 方法,输出其名称、作者、价格、类型、借阅状态等。
    //ShowOperaetion.java
    package operation;import book.Book;
    import book.Booklist;import java.util.Scanner;public class ShowOperation implements IOPeration {@Overridepublic void work(Booklist booklist,Scanner scanner) {int usedSize = booklist.getUsedSize();if (usedSize == 0) {System.out.println("图书列表为空!");} else {for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);System.out.println("书名: " + book.getName());System.out.println("作者: " + book.getAuthor());System.out.println("价格: " + book.getPrice());System.out.println("类型: " + book.getType());System.out.println("是否借出: " + (book.isBorrowed() ? "是" : "否"));System.out.println("-------------------");}}}
    }

     work方法实现图书信息输出

    @Override
    public void work(Booklist booklist, Scanner scanner) {int usedSize = booklist.getUsedSize();if (usedSize == 0) {System.out.println("图书列表为空!");} else {for (int i = 0; i < usedSize; i++) {Book book = booklist.getBook(i);System.out.println("书名: " + book.getName());System.out.println("作者: " + book.getAuthor());System.out.println("价格: " + book.getPrice());System.out.println("类型: " + book.getType());System.out.println("是否借出: " + (book.isBorrowed() ? "是" : "否"));System.out.println("-------------------");}}
    }
    • @Override:注解表明该方法是重写 IOPeration 接口中的 work 方法。
    • int usedSize = booklist.getUsedSize();:获取图书列表中实际存在的图书数量。
    • if (usedSize == 0):检查图书列表是否为空,如果为空则输出提示信息。
    • for (int i = 0; i < usedSize; i++):如果图书列表不为空,使用 for 循环遍历图书列表。
    • Book book = booklist.getBook(i);:从图书列表中获取当前索引对应的图书对象。
    • 接下来的一系列 System.out.println 语句用于输出每本图书的详细信息,包括书名、作者、价格、类型和借阅状态,并使用分隔线 "-------------------" 分隔不同图书的信息。

    3. user模块实现

    在user模块中,我们需要实现管理员和普通用户两个模块。

    首先,我们来创建一个user模块,用来管理用户;

    package user;import book.Booklist;
    import operation.IOPeration;
    import java.util.Scanner;public abstract class User {protected String name;public IOPeration[] IOPerations;public User(String name) {this.name = name;}public abstract int menu(Scanner scanner);public void doOperation(int choice, Booklist booklist, Scanner scanner) {IOPerations[choice].work(booklist, scanner);}
    }

    public abstract class User中:

    • abstract:表示这是一个抽象类,不能直接创建实例(需通过子类继承并实现抽象方法后使用)。
    • 作用:作为所有用户类型(管理员、普通用户)的父类,定义公共属性和行为规范。
    public User(String name) {this.name = name;
    }

    这段代码中,我们使用了抽象方法menu,用来显示不同用户的菜单(管理员菜单 / 普通用户菜单),并通过 Scanner 获取用户输入的操作选项(如 1 表示 “查找图书”,0 表示 “退出”)。

    我们使用 doOperation方法根据用户选择的菜单选项,执行对应的操作。

    public void doOperation(int choice, Booklist booklist, Scanner scanner) {IOPerations[choice].work(booklist, scanner);
    }
    • choice:用户输入的选项(如 1 对应 IOPerations[1])。
    • booklist:图书列表对象,用于操作图书数据(如添加、删除、借阅)。
    • scanner:扫描器对象,用于在操作中获取用户输入(如新增图书时输入书名)。
    • 通过 IOPerations[choice] 找到对应的操作类(如 FindOperation),调用其 work 方法(定义在 IOPeration 接口中),实现具体功能(如查找图书)。
    • 通过 IOPerations 数组差异化配置不同用户的可执行操作,实现了管理员与普通用户的功能分离。

      下面让我们来看管理员模块,AdminUser.java

    package user;import operation.*;
    import java.util.Scanner;public class AdminUser extends User {public AdminUser(String name) {super(name);this.IOPerations = new IOPeration[]{new ExitOperation(),new FindOperation(),new AddOperation(),new DelOperation(),new ShowOperation()};}public int menu(Scanner scanner) {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("0.退出系统");System.out.println("*************************");System.out.println("请输入你的操作:");return scanner.nextInt();}
    }

    AdminUser.java继承了他的父类user,并且必须实现 User 类中定义的抽象方法,,实现了管理员用户的特定功能。

    public AdminUser(String name) {super(name);this.IOPerations = new IOPeration[]{new ExitOperation(),new FindOperation(),new AddOperation(),new DelOperation(),new ShowOperation()};
    }
    • super(name);:调用父类 User 的构造方法,将管理员的姓名传递给父类进行初始化。
    • this.IOPerations = ...:初始化 IOPerations 数组,该数组存储了管理员用户可以执行的操作。数组中的每个元素都是 IOPeration 接口的实现类的实例,分别对应退出系统、查找图书、新增图书、删除图书和显示图书这五个操作。
    • 简而言之就是管理员系统通过IOPeration 接口来删除增加查找图书。

    下面让我们来看普通用户模块,NormalUser.java

    package user;import operation.*;
    import java.util.Scanner;public class NormalUser extends User {public NormalUser(String name) {super(name);this.IOPerations = new IOPeration[]{new ExitOperation(),new FindOperation(),new BorrowedOperation(),new ReturnOperation()};}public int menu(Scanner scanner) {System.out.println("*************************");System.out.println("Hello" + this.name + "欢迎来到普通用户菜单");System.out.println("1.查找图书");System.out.println("2.借阅图书");System.out.println("3.归还图书");System.out.println("0.退出系统");System.out.println("*************************");System.out.println("请输入你的操作:");return scanner.nextInt();}
    }

    NormalUser 类代表了图书管理系统中的普通用户,通过继承 User 类,它继承了用户的基本属性和行为。在构造方法中,初始化了普通用户可以执行的操作列表;在 menu 方法中,显示了普通用户的菜单并获取用户的操作选择。


    四、主菜单 

    import book.Booklist;
    import user.AdminUser;
    import user.NormalUser;
    import user.User;import java.util.Scanner;public class Main {public static User login(Scanner scanner) {System.out.println("请输入你的姓名:");String name = scanner.nextLine();int choice;while (true) {System.out.println("请输入你的身份:1. 管理员 0. 普通用户");try {choice = scanner.nextInt();scanner.nextLine(); // 消耗掉换行符if (choice == 0 || choice == 1) {break;} else {System.out.println("输入无效,请输入 0 或 1。");}} catch (Exception e) {System.out.println("输入无效,请输入一个整数。");scanner.nextLine(); // 清除无效输入System.out.println("异常信息: " + e.getMessage());e.printStackTrace(); // 打印异常的堆栈跟踪信息}}if (choice == 1) {return new AdminUser(name);} else {return new NormalUser(name);}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);Booklist booklist = new Booklist();User user = login(scanner);while (true) {int choice = user.menu(scanner);user.doOperation(choice, booklist, scanner);}}
    }

    菜单部分就比较简单,login方法

    • 用户姓名输入:提示用户输入姓名,并且读取用户输入的姓名。
    • 用户身份选择:提示用户输入身份(1 代表管理员,0 代表普通用户),同时对用户输入进行有效性验证。若输入无效,会提示用户重新输入。
    • 用户对象创建:依据用户输入的身份,创建对应的 AdminUser 或者 NormalUser 对象。
    • Exception:这是 Java 中所有异常类的父类。Java 里的异常类构建了一个类层次结构,Exception 处于这个层次结构的核心位置,许多具体的异常类都继承自它,像 IOExceptionNullPointerExceptionInputMismatchException 等。
    • e:这是一个引用变量,其类型为 Exception。在异常捕获时,它会指向实际抛出的异常对象。借助这个引用变量,你能够访问异常对象的属性和方法,进而获取异常的相关信息,例如异常的类型、错误信息等。
    • 若用户输入的不是有效的整数,scanner.nextInt() 就会抛出 InputMismatchException 异常。
    • try 块里尝试读取用户输入的整数。
    • 若用户输入的不是有效的整数,scanner.nextInt() 就会抛出 InputMismatchException 异常。
    • catch (Exception e) 捕获这个异常,将异常对象赋值给 e
    • 接着可以通过 e.getMessage() 获取异常的详细信息,使用 e.printStackTrace() 打印异常的堆栈跟踪信息,这有助于调试程序。

    main方法中,我们创建 Scanner 对象用于读取用户输入,创建 Booklist 对象来管理图书列表,调用 login 方法完成用户登录。

    项目地址

    booktest · 王磊鑫/JAVA - 码云 - 开源中国

    相关文章:

    重返JAVA之路——图书管理系统

    目录 一、功能介绍 二、设计模块 三、系统构建 1.book模块 2.operation模块 输入循环和验证 查找图书并处理借阅状态 未找到图书的处理 查找删除图书功能实现 未找到图书的处理 图书查找与归还 work方法实现图书信息输出 3. user模块实现 四、主菜单 一、功能介绍 …...

    【16】数据结构之基于树的排序算法篇章

    目录标题 选择排序简单选择排序树形选择排序 堆排序堆的定义Heap小跟堆大根堆堆的存储堆的代码设计堆排序的代码设计 排序算法综合比较 选择排序 基本思想&#xff1a;从待排序的序列中选出最大值或最小值&#xff0c;交换该元素与待排序序列的头部元素&#xff0c;对剩下的元…...

    Uniapp:确认框

    目录 一、 出现场景二、 效果展示三、具体使用 一、 出现场景 在项目的开发中&#xff0c;会经常出现删除数据的情况&#xff0c;如果直接删除的话&#xff0c;可能会存在误删&#xff0c;用户体验不好&#xff0c;所以需要增加一个消息提示&#xff0c;提醒用户是否删除。 二…...

    pyswmm实现洪涝模拟

    准备好.inp文件作为SWMM模型输入&#xff0c;调用pyswmm模块执行模拟&#xff0c;返回节点溢流量&#xff08;flooding&#xff09;作为积水量的初步表征。 代码&#xff1a; from pyswmm import Simulation, Nodes import pandas as pddef run_swmm_simulation(inp_file, ou…...

    My Diary Pro:记录生活,珍藏回忆

    我的日记My Diary Pro是一个非常好用的手机日记软件&#xff0c;可以使用它来记录每日生活日常&#xff0c;不少的用户可能都知道在生活之中可能会发生一些比较的重要的事情&#xff0c;实际上我们都可以将这些内容记录下来。包括个人观点&#xff0c;旅行游记&#xff0c;心情…...

    CSRF(跨站请求伪造)漏洞概述

    CSRF(跨站请求伪造)漏洞概述 一、​什么是 CSRF ​ 攻击者诱导已登录用户在不知情的情况下&#xff0c;对受信任网站执行未授权操作。 简单说&#xff1a;你登录着网站A&#xff0c;攻击者诱导你访问某个恶意链接&#xff0c;使网站A误以为是你自己发出的操作&#xff08;比…...

    [Java实战经验]对象拷贝

    目录 谨慎重写clone方法重写clone()支持深拷贝带来的问题 合适的深拷贝 首先&#xff0c;对于不可变的类&#xff0c;我们不应该实现Cloneable接口&#xff0c;因为不可变类不需要拷贝&#xff0c;直接引用即可&#xff0c;实现Cloneable接口只会造成浪费。 对于Java可变类来说…...

    WAF防火墙:构筑Web应用安全的“隐形护盾”

    在数字化时代&#xff0c;Web应用已成为企业服务与用户交互的核心窗口。然而&#xff0c;随之而来的SQL注入、跨站脚本攻击&#xff08;XSS&#xff09;、DDoS攻击等威胁&#xff0c;时刻考验着网站的安全防线。Web应用防火墙&#xff08;WAF&#xff09;作为关键防护工具&…...

    开源智慧巡检——无人机油田AI视频监控的未来之力

    油田巡检&#xff0c;关乎能源命脉&#xff0c;却常受困于广袤地形、高危环境和人工效率瓶颈。管道泄漏、设备故障、非法闯入——这些隐患稍有疏忽&#xff0c;便可能酿成大患。传统巡检已无法满足现代油田对安全与效率的需求&#xff0c;而无人机油田巡检系统正以智能化之力重…...

    【2025年泰迪杯数据挖掘挑战赛】B题 完整论文 模型建立与求解

    目录 2025年泰迪杯数据挖掘挑战赛 B题完整论文&#xff1a;建模与求解 Matlab代码一、问题重述二、模型假设与符号说明2.1 模型基本假设2.2 符号说明 三、数据预处理**问题一&#xff1a;志愿者身体活动信息的统计分析****问题二&#xff1a;身体活动MET值的实时估计模型构建**…...

    Chromium 134 编译指南 macOS篇:安装 Xcode(二)

    1. 引言 在Chromium开发的征程中&#xff0c;为macOS平台构建正确的开发环境是成功编译的关键基础。继上一篇系统环境准备后&#xff0c;本文将重点介绍Xcode的安装与配置过程。作为macOS上不可或缺的集成开发环境(IDE)&#xff0c;Xcode为Chromium 134的编译提供了必要的编译…...

    软件定义网络(SDN):重塑未来网络的革命性架构

    在当今数字化时代&#xff0c;网络已成为企业、云计算、5G通信和物联网&#xff08;IoT&#xff09;的核心基础设施。然而&#xff0c;传统网络架构由于其封闭、静态和分布式的特性&#xff0c;难以应对快速变化的业务需求。软件定义网络&#xff08;Software-Defined Networki…...

    Java虚拟机面试题:类加载机制

    &#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

    OCCT 入门(3)核心模块与架构

    文章目录 一、核心模块与架构1、架构概述2、核心模块3、数据流转3.1、几何创建&#xff08;Geometric Primitives&#xff09;3.2、拓扑构建&#xff08;Topology Construction&#xff09;3.3、模型处理&#xff08;Modeling Algorithms&#xff09;3.4、可视化&#xff08;Vi…...

    MAC-​​需求​​:10万订单异步执行库存扣减、短信通知。

    批量任务并行处理​​ 实现,通过拆分任务、异步执行和线程池管理提升处理。 ​​10万订单异步处理方案设计​​ 基于图中代码的批量处理框架,结合订单业务需求,以下是 ​​库存扣减与短信通知的异步实现​​: ​​1. 代码实现(基于原有框架改造)​​ @Service public…...

    ArrayList vs LinkedList,HashMap vs TreeMap:如何选择最适合的集合类?

    精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 在 Java 开发中&#xff0c;集合类的选择直接影响程序的性能和代码的可维护性。不同的数据结构适用于不同的场景&#xff0c;盲目使用可能导致内存浪费、性能…...

    使用Form.List且有Select组件

    当在使用Form.List组件&#xff0c;且组件中有Select选项时&#xff0c;针对每一次选择&#xff0c;都要过滤掉那些已经选择过的选项&#xff0c;可能遇到的问题&#xff1a; 直接过滤会将每一个Select中的options选项都过滤掉&#xff0c;无法正常展示选择的选项 解决办法&a…...

    大数据学习笔记

    文章目录 1. 大数据概述1.1 大数据的特性1.2 大数据技术生态1.2.1 Hadoop 的概念特性1.2.2 Hadoop生态圈 — 核心组件与技术栈1.2.3 Hadoop生态演进趋势 2. 数据处理流程与技术栈2.1 数据采集2.1.1 日志采集工具2.1.2 实时数据流2.1.3 数据迁移 2.2 数据预处理2.2.1 批处理2.2.…...

    Obsidian 文件夹体系构建 -INKA

    Obsidian 文件夹体系构建 -INKA 本篇文章主要分享一下自己折腾学习实践过的 INKA 框架方法。原地址&#xff1a;Obsidian文件夹体系构建–INKA。 文章目录 Obsidian 文件夹体系构建 -INKA前言INKA简介INKA 理论最佳实践实际应用 反思 前言 上文 Obsidian文件夹体系构建-ACCES…...

    QML与C++:基于ListView调用外部模型进行增删改查(性能优化版)

    目录 引言相关阅读工程结构数据模型设计DataModel 类ContactProxyModel 类 为什么使用QSortFilterProxyModel&#xff1f;应用初始化与模型连接UI实现 性能分析与优化运行效果扩展思考总结下载链接 引言 在上一篇中介绍了基于ListView调用外部模型进行增删改查&#xff0c;本文…...

    集合常用Stream操作

    1、中间操作 filter()过滤 将流中的元素筛选出满足条件的元素 List<String> list Arrays.asList("abc","test","demo","frse","fesfes"); list.stream().filter(s -> s.startsWith("f")).forEach(Sy…...

    ReactNative中处理安全区域问题

    RN原生方案不支持android系统&#xff0c;所以在此使用三方组件react-native-safe-area-context 1、安装插件 yarn add react-native-safe-area-context2、安装完成后直接yarn ios可能会失败&#xff0c;需要先 cd ios && pod install && cd ..出来再继…...

    二、The Power of LLM Function Calling

    一、Function Calling 的诞生背景 1. 传统LLM的局限性 静态文本生成的不足&#xff1a;早期的LLM&#xff08;如早期版本的ChatGPT&#xff09;主要依赖预训练的知识库生成文本&#xff0c;但无法直接与外部系统或API交互。这意味着它们只能基于历史数据回答问题&#xff0c;…...

    贪心算法day10(无重叠区间)

    1.无重叠区间 435. 无重叠区间 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 代码&#xff1a; class Solution {public static int eraseOverlapIntervals(int[][] intervals) {Arrays.sort(intervals,(v1,v2)->{return v1[0]-v2[0];});int left interva…...

    reactive 解构赋值给 ref

    在 Vue 3 中&#xff0c;当你执行以下操作时&#xff1a; javascript const applyBasicInfo ref(); applyBasicInfo.value { ...props.applyBasicInfo }; 最终的 applyBasicInfo.value 是响应式对象&#xff0c;但与原对象 props.applyBasicInfo 的响应性完全独立&#xf…...

    MongoDB简单用法

    图片中 MongoDB Compass 中显示了默认的三个数据库&#xff1a; adminconfiglocal 如果在 .env 文件中配置的是&#xff1a; MONGODB_URImongodb://admin:passwordlocalhost:27017/ MONGODB_NAMERAGSAAS&#x1f4a1; 一、为什么 Compass 里没有 RAGSAAS 数据库&#xff1f;…...

    日语学习-日语知识点小记-构建基础-JLPT-N4阶段(4): 可能形(かのうけい)

    日语学习-日语知识点小记-构建基础-JLPT-N4阶段(4): 可能形(かのうけい) 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)~んです  復習(ふくしゅう)(2)いただけませんか 復習(ふくしゅう)(3)可能形(かのうけい)(1)1グループ:(2)2グループ…...

    Windows 下 MongoDB ZIP 版本安装指南

    在开发和生产环境中&#xff0c;MongoDB 是一种非常流行的 NoSQL 数据库&#xff0c;以其灵活性和高性能而受到开发者的青睐。对于 Windows 用户来说&#xff0c;MongoDB 提供了多种安装方式&#xff0c;其中 ZIP 版本因其灵活性和轻量级的特点&#xff0c;成为很多开发者的首选…...

    万字长篇————C语言指针学习汇总

    经过一段时间的学习&#xff0c;我们已经接触到了C语言的很多知识了。不过目前我们接下来我们要接触C语言中一个最大的“门槛”&#xff1a;指针。 什么是指针&#xff1f; 在介绍指针之前&#xff0c;我们首先要明白变量与地址之间的关系。 举一个生活中的案例&#xff1a;一…...

    day29图像处理OpenCV

    文章目录 一、图像预处理6 图像色彩空间转换6.3灰色/BGR/HSV相互转化 7 彩图转灰图方法7.1 最大值法7.2 平均值法7.3 加权均值法7.4 案例 8 图像二值化处理8.1 阈值法(typecv2.THRESH_BINARY)8.2 反阈值法(THRESH_BINARY_INV)8.3 截断阈值法(THRESH_TRUNC)8.4 低阈值零处理(THR…...

    Spring Boot 项目三种打印日志的方法详解。Logger,log,logger 解读。

    目录 一. 打印日志的常见三种方法&#xff1f; 1.1 手动创建 Logger 对象&#xff08;基于SLF4J API&#xff09; 1.2 使用 Lombok 插件的 Slf4j 注解 1.3 使用 Spring 的 Log 接口&#xff08;使用频率较低&#xff09; 二. 常见的 Logger&#xff0c;logger&#xff0c;…...

    KrillinAI:视频跨语言传播的一站式AI解决方案

    引言 在全球内容创作领域&#xff0c;跨语言传播一直是内容创作者面临的巨大挑战。传统的视频本地化流程繁琐&#xff0c;涉及多个环节和工具&#xff0c;不仅耗时耗力&#xff0c;还常常面临质量不稳定的问题。随着大语言模型(LLM)技术的迅猛发展&#xff0c;一款名为Krillin…...

    PDF处理控件Aspose.PDF指南:使用 C# 从 PDF 文档中删除页面

    需要从 PDF 文档中删除特定页面&#xff1f;本快速指南将向您展示如何仅用几行代码删除不需要的页面。无论您是清理报告、跳过空白页&#xff0c;还是在共享前自定义文档&#xff0c;C# 都能让 PDF 操作变得简单高效。学习如何以编程方式从 PDF 文档中选择和删除特定页面&#…...

    在 IntelliJ IDEA 中开发 Java Web 项目时,遇到包内明明存在某个类但类名仍然爆红(显示红色错误提示)

    在 IntelliJ IDEA 中开发 Java Web 项目时&#xff0c;遇到包内明明存在某个类但类名仍然爆红&#xff08;显示红色错误提示&#xff09;&#xff0c;而项目却能正常运行&#xff0c;重启 IDEA 后问题依旧&#xff0c;这通常是由以下原因及解决方法导致的&#xff1a; 1. 缓存…...

    【4】k8s集群管理系列--harbor镜像仓库本地化搭建

    一、harbor基本概念 ‌Harbor是一个由VMware开源的企业级Docker镜像仓库解决方案‌&#xff0c;旨在解决企业在容器化应用部署中的痛点&#xff0c;提供镜像存储、管理、安全和分发的全生命周期管理‌。Harbor扩展了Docker Registry&#xff0c;增加了企业级功能&#xff0c;如…...

    Active Directory域服务管理与高级应用技术白皮书

    目录 一、Active Directory核心架构解析 1.1 AD域服务核心组件 1.2 域功能级别演进 1.3 AD LDS应用场景 二、企业级域环境部署最佳实践 2.1 域控制器部署规划 2.2 高可用架构设计 2.3 客户端入域优化 三、高级域管理技术 3.1 精细化权限管理 3.2 组策略深度配置 3.3…...

    OCP中的OCS operator介绍及应用示例

    一、OCS operator介绍 在 Red Hat OpenShift Container Platform&#xff08;OCP4.8版之前&#xff0c;包含4.8&#xff09; 中&#xff0c;OCS Operator&#xff08;OpenShift Container Storage Operator&#xff09; 是用于在 OpenShift 集群中部署、配置和管理 OpenShift …...

    Linux-服务器添加审计日志功能

    #查看audit软件是否在运行(状态为active而且为绿色表示已经在运行) systemctl start auditd #如果没有在运行的话,查看是否被系统禁用 (audit为0表示被禁用) cat /proc/cmdline | grep -w "audit=0" #修改/etc/default/grub里面audit=0 改为audit=1 #更新GRUB…...

    ARM Cortex-M中断处理全解析

    今天我们聊一聊ARM Cortex-M中断处理。在嵌入式系统中&#xff0c;中断是实现实时响应的核心机制。想象一下&#xff0c;如果没有中断&#xff1a; 按键按下时&#xff0c;系统可能忙于其他任务而错过响应通信数据到来时&#xff0c;可能因为没及时处理而丢失定时任务难以精确…...

    douyin_search_tool | 用python开发的抖音关键词搜索采集软件

    本软件工具仅限于学术交流使用&#xff0c;严格遵循相关法律法规&#xff0c;符合平台内容合法性&#xff0c;禁止用于任何商业用途&#xff01; 抖音作为国内颇受欢迎的短视频社交平台&#xff0c;汇聚了大量用户群体和活跃用户。分析平台上的热门视频可用于市场调研和竞品分析…...

    基于FreeBSD的Unix服务器网络配置

    Unix系统版本 FreeBSD-10.1-i386 网络配置 1.配置网络ip及网关 #编辑配置文件 ee /etc/rc.conf #参照如下内容设置 ifconfig_em0”inet 192.168.1.189 netmask 255.255.255.0” defaultrouter”192.168.1.1” #回到命令模式 esc #保存 a a 2.配置dns #编辑配置文件 ee /etc/…...

    Margin和Padding在WPF和CSS中的不同

    CSS和WPF中 margin 与 padding 在方向上的规定基本一致&#xff0c;但在使用场景和一些细节上有所不同。 CSS - 方向规定&#xff1a; margin 和 padding 属性可以分别指定上、右、下、左四个方向的值。例如 margin:10px 20px 30px 40px; 表示上外边距为10px、右外边距为20…...

    JVM 概述

    JVM概述 JVM的全为 Java Virtual Machine&#xff0c;但是目前的 JVM 已经不再与任何语言进行深度耦合了&#xff0c;其本质就是运行在计算机上的程序&#xff0c;职责是运行处理 Java 字节码文件。 JVM 功能 解释和运行 JVM 会对字节码文件中的指令&#xff0c;实时的解释为…...

    基于django云平台的求职智能分析系统(源码+lw+部署文档+讲解),源码可白嫖!

    摘要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;招聘信息管理系统当然不能排除在外。求职智能分析系统是在实际应用和软件工程的开发原理之上&#xff0c;运用Python语言、爬虫技术以及Dj…...

    在 Ubuntu 上通过 Docker 部署 Misskey 服务器

    在这篇博客中&#xff0c;我们将探讨如何在 Ubuntu 上通过 Docker 部署 Misskey 服务器。Misskey 是一个开源的社交网络平台&#xff0c;支持丰富的社交功能&#xff0c;适合个人和小型社群使用。而 Docker 则是一个便捷的容器化平台&#xff0c;允许开发者轻松地打包、发布和运…...

    Pytorch 第十五回:神经网络编码器——GAN生成对抗网络

    Pytorch 第十五回&#xff1a;神经网络编码器——GAN生成对抗网络 本次开启深度学习第十五回&#xff0c;基于Pytorch的神经网络编码器。本回分享的是GAN生成对抗网络。在本回中&#xff0c;通过minist数据集来分享如何建立一个GAN生成对抗网络。接下来给大家分享具体思路。 本…...

    gitlab如何查看分支的创建时间

    在 GitLab 上查看分支创建时间&#xff0c;常规的界面不会直接显示&#xff0c;但可以通过以下几种方法查到准确时间&#xff1a; 方法一&#xff1a;通过 GitLab Web 界面查看首次提交时间&#xff08;近似&#xff09; 打开你的项目仓库。点击左侧的「Repository&#xff08…...

    centos时间不正确解决

    检查当前系统时间 date如果时间明显不正确&#xff0c;可以进一步检查硬件时钟&#xff08;BIOS 时间&#xff09;&#xff1a; bash复制代码hwclock --show同步时间&#xff08;推荐方式&#xff09; 为了确保系统时间准确&#xff0c;建议使用 NTP&#xff08;网络时间协议…...

    ubuntu启动 Google Chrome 时默认使用中文界面,设置一个永久的启动方式

    方法 &#xff1a;通过桌面快捷方式设置 编辑 Chrome 的桌面快捷方式&#xff1a; 找到您的 Google Chrome 快捷方式文件。如果是通过菜单启动&#xff0c;通常会在以下路径找到与 Chrome 相关的 .desktop 文件&#xff1a; sudo vim /usr/share/applications/google-chrome.d…...

    opencv腐蚀的操作过程

    在腐蚀操作的详细流程中&#xff0c;遍历图像的过程如下&#xff1a; 初始化&#xff1a; 设置一个起始位置&#xff08;通常从图像的左上角开始&#xff09;。 准备好结构元素&#xff08;structuring element&#xff09;&#xff0c;它是一个小的矩阵&#xff0c;大小通常是…...