第三阶段:项目实战①小型项目开发(上篇)
📋 阶段概述
欢迎来到Java学习的第三阶段!在前两个阶段中,您已经掌握了Java的基础语法和面向对象编程的核心概念。现在是时候将这些知识付诸实践,通过开发真实的小型项目来巩固和提升您的编程技能。
本阶段将通过两个经典的小型项目——学生管理系统(控制台版)和简易图书管理系统(Swing GUI),帮助您从理论走向实践,体验完整的软件开发流程。
🎯 学习目标
⏱️ 预计学习时间:6-8小时
- 📖 理论学习:1-2小时(项目规划和技术选型)
- 💻 代码实践:4-5小时(控制台应用开发)
- 🧪 测试调试:1小时(单元测试和运行验证)
完成本阶段学习后,您将能够:
- 项目规划能力:学会分析需求、设计系统架构、规划开发流程
- 综合应用技能:熟练运用面向对象编程思想解决实际问题
- 代码组织能力:掌握良好的代码结构和项目组织方式
- 调试和测试:具备基本的程序调试和错误处理能力
- 用户界面开发:了解控制台和GUI界面的开发方法
- 数据管理:掌握基本的数据存储和检索技术
📈 预期成果
- 完成2个功能完整的小型项目
- 建立良好的编程习惯和代码风格
- 具备独立开发简单应用程序的能力
- 为后续学习数据库、Web开发等高级主题打下坚实基础
🗂️ 项目选择和规划指导
项目开发流程
- 需求分析:明确项目功能和用户需求
- 系统设计:设计类结构和模块划分
- 编码实现:按模块逐步实现功能
- 测试调试:验证功能正确性
- 优化完善:改进用户体验和代码质量
技术选型原则
- 循序渐进:从简单到复杂,逐步增加技术难度
- 实用导向:选择实际开发中常用的技术和模式
- 可扩展性:为后续功能扩展预留空间
🚀 项目一:学生管理系统(控制台版)
项目概述
开发一个基于控制台的学生信息管理系统,实现学生信息的增删改查功能。这是一个经典的CRUD(Create, Read, Update, Delete)应用,非常适合初学者练习面向对象编程。
📸 效果预览
以下是学生管理系统的运行效果图:
1. 应用程序启动界面

应用程序启动成功,显示欢迎信息和系统初始化状态
2. 主菜单界面

清晰的菜单选项,提供完整的学生管理功能
3. 显示所有学生功能

格式化的表格显示,包含学生的完整信息
4. 统计信息功能

详细的数据统计分析,包括总数、平均分、分数分布等
技术要求
- 核心框架:Spring Boot 3.2+
- 数据持久化:Spring Data JPA、Hibernate 6.x
- 数据库:MySQL 8.0+
- Java版本:JDK 17+(Spring Boot 3.x最低要求)
- 连接池:HikariCP(Spring Boot默认)
- 构建工具:Maven
- 开发工具:Spring Boot DevTools
- 设计模式:Repository模式、Service模式、依赖注入
功能需求
核心功能
-
学生信息管理
- 添加学生信息
- 查看学生列表
- 修改学生信息
- 删除学生信息
- 按条件搜索学生
-
数据持久化
- 使用Spring Data JPA进行数据持久化
- 通过Repository接口实现CRUD操作
- 利用JPA注解进行对象关系映射
-
用户交互
- 友好的菜单界面
- 输入验证和错误提示
环境准备
项目初始化
使用Spring Initializr创建Spring Boot项目,或手动创建Maven项目。
重要提醒:Spring Boot 3.x的重要变化:
- 最低JDK版本:要求JDK 17或更高版本
- Jakarta EE:从javax.*迁移到jakarta.*包名
- 原生编译:支持GraalVM原生镜像编译
- 可观测性:增强的监控和追踪功能
Maven依赖配置
在pom.xml中添加以下依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>org.chapter09</groupId>
<artifactId>chapter09_01</artifactId>
<version>1.0.0</version>
<name>Chapter09_01 - Student Management Console</name>
<description>学生管理系统(控制台版)</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Data JPA Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot DevTools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Spring Boot Test Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Validation Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<!-- H2数据库用于测试 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- JaCoCo Plugin for code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
数据库准备
- 安装MySQL数据库
- 创建数据库:
-- 创建数据库
CREATE DATABASE student_management CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
应用配置
创建src/main/resources/application.yml配置文件:
spring:
datasource:
url: jdbc:mysql://localhost:${MYSQL_PORT}/student_management?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
username: root
password: ${MYSQL_PASSWORD} # 请修改为实际密码,我这里使用环境变量
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # 自动创建/更新表结构
show-sql: true # 显示SQL语句
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
# 应用配置
application:
name: student-management
# 控制台应用配置
main:
web-application-type: none # 禁用Web功能
# 日志配置
logging:
level:
org.chapter09: DEBUG
org.hibernate.SQL: DEBUG
实现步骤
第一步:主启动类
🔍 学习要点:
- @SpringBootApplication注解 - 这是Spring Boot的核心注解,包含了@Configuration、@EnableAutoConfiguration和@ComponentScan
- SpringApplication.run() - 启动Spring Boot应用的标准方法
- 控制台应用 - 通过CommandLineRunner实现非Web应用
package org.chapter09.studentmanagement;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 学生管理系统主启动类(控制台版)
* <p>
* 基于Spring Boot的控制台应用程序,展示了非Web应用的开发模式。
*
* <h3>🔍 学习要点:</h3>
* <ul>
* <li><strong>@SpringBootApplication</strong> - Spring Boot核心注解</li>
* <li><strong>控制台应用</strong> - 非Web应用的开发方式</li>
* <li><strong>自动配置</strong> - Spring Boot的自动配置机制</li>
* </ul>
*/
@SpringBootApplication
public class StudentManagementConsoleApplication {
public static void main(String[] args) {
SpringApplication.run(StudentManagementConsoleApplication.class, args);
System.out.println("学生管理系统(控制台版)启动成功!");
}
}
第二步:学生实体类
🔍 学习要点:
- JPA实体映射 - 使用@Entity、@Table、@Column等注解进行对象关系映射
- 数据类型选择 - BigDecimal用于精确的数值计算,避免浮点数精度问题
- 时间戳管理 - @CreationTimestamp和@UpdateTimestamp自动管理创建和更新时间
- 数据验证 - 使用Bean Validation注解进行数据校验
package org.chapter09.studentmanagement.entity;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 学生信息实体类
*/
@Entity
@Table(name = "t_students")
public class Student {
@Id
@Column(name = "id_", length = 20)
private String id; // 学号
@Column(name = "name_", nullable = false, length = 50)
private String name; // 姓名
@Column(name = "age_", nullable = false)
private Integer age; // 年龄
@Column(name = "gender_", length = 10)
private String gender; // 性别
@Column(name = "major_", length = 100)
private String major; // 专业
@Column(name = "score_", precision = 5, scale = 2)
private BigDecimal score; // 成绩
@CreationTimestamp
@Column(name = "created_time_", updatable = false)
private LocalDateTime createdTime; // 创建时间
@UpdateTimestamp
@Column(name = "updated_time_")
private LocalDateTime updatedTime; // 更新时间
// 默认构造方法
public Student() {}
// 带参构造方法
public Student(String id, String name, Integer age, String gender, String major, BigDecimal score) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.major = major;
this.score = score;
}
// getter和setter方法
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getMajor() { return major; }
public void setMajor(String major) { this.major = major; }
public BigDecimal getScore() { return score; }
public void setScore(BigDecimal score) { this.score = score; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
public LocalDateTime getUpdatedTime() { return updatedTime; }
public void setUpdatedTime(LocalDateTime updatedTime) { this.updatedTime = updatedTime; }
@Override
public String toString() {
return String.format("学号: %s, 姓名: %s, 年龄: %d, 性别: %s, 专业: %s, 成绩: %.2f",
id, name, age, gender, major, score);
}
}
第三步:数据访问层(Repository)
package org.chapter09.studentmanagement.repository;
import org.chapter09.studentmanagement.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.List;
/**
* 学生数据访问接口
*/
@Repository
public interface StudentRepository extends JpaRepository<Student, String> {
/**
* 根据姓名查找学生
*/
List<Student> findByNameContaining(String name);
/**
* 根据专业查找学生
*/
List<Student> findByMajorContaining(String major);
/**
* 根据性别查找学生
*/
List<Student> findByGender(String gender);
/**
* 根据成绩范围查找学生
*/
List<Student> findByScoreBetween(BigDecimal minScore, BigDecimal maxScore);
/**
* 根据年龄范围查找学生
*/
List<Student> findByAgeBetween(Integer minAge, Integer maxAge);
/**
* 自定义查询:根据关键词搜索学生(姓名或专业)
*/
@Query("SELECT s FROM Student s WHERE s.name LIKE %:keyword% OR s.major LIKE %:keyword%")
List<Student> searchByKeyword(@Param("keyword") String keyword);
/**
* 自定义查询:根据专业统计学生数量
*/
@Query("SELECT s.major, COUNT(s) FROM Student s GROUP BY s.major")
List<Object[]> countStudentsByMajor();
/**
* 自定义查询:查找成绩优秀的学生(成绩>=90)
*/
@Query("SELECT s FROM Student s WHERE s.score >= 90 ORDER BY s.score DESC")
List<Student> findExcellentStudents();
}
第四步:业务逻辑层(Service)
package org.chapter09.studentmanagement.service;
import org.chapter09.studentmanagement.entity.Student;
import org.chapter09.studentmanagement.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
/**
* 学生管理业务逻辑类
*/
@Service
@Transactional
public class StudentService {
@Autowired
private StudentRepository studentRepository;
/**
* 添加学生
*/
public Student addStudent(Student student) {
// 检查学号是否已存在
if (studentRepository.existsById(student.getId())) {
throw new RuntimeException("学号已存在:" + student.getId());
}
// 数据验证
validateStudent(student);
return studentRepository.save(student);
}
/**
* 根据学号查找学生
*/
public Student findStudentById(String id) {
Optional<Student> student = studentRepository.findById(id);
return student.orElse(null);
}
/**
* 获取所有学生
*/
public List<Student> getAllStudents() {
return studentRepository.findAll();
}
/**
* 更新学生信息
*/
public Student updateStudent(Student student) {
// 检查学生是否存在
if (!studentRepository.existsById(student.getId())) {
throw new RuntimeException("学生不存在:" + student.getId());
}
// 数据验证
validateStudent(student);
return studentRepository.save(student);
}
/**
* 删除学生
*/
public void deleteStudent(String id) {
if (!studentRepository.existsById(id)) {
throw new RuntimeException("学生不存在:" + id);
}
studentRepository.deleteById(id);
}
/**
* 搜索学生
*/
public List<Student> searchStudents(String keyword) {
return studentRepository.searchByKeyword(keyword);
}
/**
* 根据姓名查找学生
*/
public List<Student> findStudentsByName(String name) {
return studentRepository.findByNameContaining(name);
}
/**
* 根据专业查找学生
*/
public List<Student> findStudentsByMajor(String major) {
return studentRepository.findByMajorContaining(major);
}
/**
* 根据性别查找学生
*/
public List<Student> findStudentsByGender(String gender) {
return studentRepository.findByGender(gender);
}
/**
* 根据成绩范围查找学生
*/
public List<Student> findStudentsByScoreRange(BigDecimal minScore, BigDecimal maxScore) {
return studentRepository.findByScoreBetween(minScore, maxScore);
}
/**
* 查找优秀学生
*/
public List<Student> findExcellentStudents() {
return studentRepository.findExcellentStudents();
}
/**
* 统计各专业学生数量
*/
public List<Object[]> countStudentsByMajor() {
return studentRepository.countStudentsByMajor();
}
/**
* 验证学生信息
*/
private void validateStudent(Student student) {
if (student.getId() == null || student.getId().trim().isEmpty()) {
throw new RuntimeException("学号不能为空");
}
if (student.getName() == null || student.getName().trim().isEmpty()) {
throw new RuntimeException("姓名不能为空");
}
if (student.getAge() == null || student.getAge() < 0 || student.getAge() > 150) {
throw new RuntimeException("年龄必须在0-150之间");
}
if (student.getScore() != null && (student.getScore() < 0 || student.getScore() > 100)) {
throw new RuntimeException("成绩必须在0-100之间");
}
}
}
第五步:控制台交互类
package org.chapter09.studentmanagement.console;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.date.DateUtil;
import org.chapter09.studentmanagement.entity.Student;
import org.chapter09.studentmanagement.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.List;
import java.util.Scanner;
/**
* 学生管理系统控制台交互类
*/
@Component
public class StudentConsoleRunner implements CommandLineRunner {
@Autowired
private StudentService studentService;
private Scanner scanner = new Scanner(System.in);
@Override
public void run(String... args) throws Exception {
System.out.println("=== 欢迎使用学生管理系统(控制台版) ===");
while (true) {
showMenu();
int choice = getChoice();
switch (choice) {
case 1:
addStudent();
break;
case 2:
displayAllStudents();
break;
case 3:
searchStudent();
break;
case 4:
updateStudent();
break;
case 5:
deleteStudent();
break;
case 6:
showStatistics();
break;
case 0:
System.out.println("感谢使用,再见!");
System.exit(0);
break;
default:
System.out.println("无效选择,请重新输入!");
}
}
}
private void showMenu() {
System.out.println("\n=== 主菜单 ===");
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.print("请选择操作: ");
}
private int getChoice() {
try {
return Integer.parseInt(scanner.nextLine().trim());
} catch (NumberFormatException e) {
return -1;
}
}
private void addStudent() {
System.out.println("\n=== 添加学生信息 ===");
System.out.print("请输入学号: ");
String id = scanner.nextLine().trim();
if (StrUtil.isBlank(id)) {
System.out.println("学号不能为空!");
return;
}
System.out.print("请输入姓名: ");
String name = scanner.nextLine().trim();
if (StrUtil.isBlank(name)) {
System.out.println("姓名不能为空!");
return;
}
System.out.print("请输入年龄: ");
Integer age;
try {
age = Integer.parseInt(scanner.nextLine().trim());
} catch (NumberFormatException e) {
System.out.println("年龄格式错误!");
return;
}
System.out.print("请输入性别: ");
String gender = scanner.nextLine().trim();
System.out.print("请输入专业: ");
String major = scanner.nextLine().trim();
System.out.print("请输入成绩: ");
BigDecimal score;
try {
score = new BigDecimal(scanner.nextLine().trim());
} catch (NumberFormatException e) {
System.out.println("成绩格式错误!");
return;
}
Student student = new Student(id, name, age, gender, major, score);
try {
studentService.addStudent(student);
System.out.println("学生信息添加成功!");
} catch (RuntimeException e) {
System.out.println("添加失败:" + e.getMessage());
}
}
// 其他方法实现...
// (为节省篇幅,完整代码请参考原文档)
}
测试与运行实践
单元测试
Service层单元测试
package org.chapter09.studentmanagement.service;
import org.chapter09.studentmanagement.entity.Student;
import org.chapter09.studentmanagement.repository.StudentRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
/**
* 学生服务层单元测试
*/
@ExtendWith(MockitoExtension.class)
class StudentServiceTest {
@Mock
private StudentRepository studentRepository;
@InjectMocks
private StudentService studentService;
private Student testStudent;
@BeforeEach
void setUp() {
testStudent = new Student();
testStudent.setId("2023001");
testStudent.setName("张三");
testStudent.setAge(20);
testStudent.setGender("男");
testStudent.setMajor("计算机科学与技术");
testStudent.setScore(85.5);
}
@Test
void testAddStudent_Success() {
// Given
when(studentRepository.existsById(anyString())).thenReturn(false);
when(studentRepository.save(any(Student.class))).thenReturn(testStudent);
// When
Student result = studentService.addStudent(testStudent);
// Then
assertNotNull(result);
assertEquals("张三", result.getName());
verify(studentRepository).existsById("2023001");
verify(studentRepository).save(testStudent);
}
@Test
void testAddStudent_DuplicateId() {
// Given
when(studentRepository.existsById(anyString())).thenReturn(true);
// When & Then
RuntimeException exception = assertThrows(RuntimeException.class,
() -> studentService.addStudent(testStudent));
assertEquals("学号已存在:2023001", exception.getMessage());
verify(studentRepository, never()).save(any());
}
}
测试配置
创建src/test/resources/application-test.yml:
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
main:
web-application-type: none
logging:
level:
org.chapter09: DEBUG
运行脚本
创建scripts/run.sh:
#!/bin/bash
echo "=== 学生管理系统(控制台版) ==="
echo "请确保MySQL数据库已启动并创建了student_management数据库"
JAR_FILE="target/student-management-console-1.0.0.jar"
if [ ! -f "$JAR_FILE" ]; then
echo "JAR文件不存在,开始编译..."
mvn clean package -DskipTests
if [ $? -ne 0 ]; then
echo "编译失败,请检查代码"
exit 1
fi
fi
echo "启动学生管理系统..."
echo "注意:这是一个控制台交互应用,请在终端中操作"
java -jar $JAR_FILE
🎯 运行效果说明
当您成功运行应用程序后,将看到如下效果:
-
启动阶段:
- 显示Spring Boot启动日志
- 数据库连接初始化
- 表结构自动创建
- 显示"学生管理系统启动成功"消息
-
主菜单阶段:
- 清晰的功能菜单选项(1-7)
- 每个选项都有明确的功能说明
- 用户可以通过数字选择相应功能
-
功能操作阶段:
- 显示学生:格式化的表格展示,包含ID、姓名、年龄、性别、专业、成绩等信息
- 添加学生:逐步引导用户输入学生信息,包含输入验证
- 统计信息:显示学生总数、平均成绩、最高/最低分、各分数段分布等
-
用户体验特点:
- 友好的中文界面
- 清晰的操作提示
- 完善的错误处理
- 优雅的数据格式化显示
💡 提示:如果您看到的效果与上方预览图不同,请检查:
- 数据库连接是否正常
- 是否有测试数据
- 控制台编码是否设置为UTF-8
项目特点
- 控制台交互:通过命令行界面进行所有操作,如上方效果图所示的清晰菜单结构
- Spring Boot集成:使用CommandLineRunner实现控制台应用,启动界面显示完整的初始化过程
- 数据持久化:基于JPA的数据库操作,支持完整的CRUD功能
- 用户友好:清晰的菜单和格式化输出,如效果图中展示的表格化数据显示
- 数据统计:提供丰富的统计功能,包括平均分、分数分布等可视化信息
- 错误处理:完善的输入验证和错误提示机制
- 中文界面:全中文操作界面,符合国内用户使用习惯
❓ 常见问题解答
Q1: 为什么使用BigDecimal而不是Double来存储成绩?
A: BigDecimal提供精确的十进制运算,避免了浮点数的精度问题。对于涉及金钱、分数等需要精确计算的场景,BigDecimal是更好的选择。
Q2: CommandLineRunner和@PostConstruct有什么区别?
A:
- CommandLineRunner: 在Spring Boot应用启动完成后执行,可以访问命令行参数
- @PostConstruct: 在Bean初始化完成后立即执行,执行时机更早
Q3: 为什么要使用@Transactional注解?
A: @Transactional确保数据库操作的原子性,如果方法执行过程中出现异常,会自动回滚事务,保证数据一致性。
Q4: 如何处理用户输入验证?
A: 可以通过以下方式:
- 使用Bean Validation注解(@NotNull、@Size等)
- 在Service层添加业务逻辑验证
- 在控制台交互层进行格式验证
Q5: 控制台应用如何优雅地退出?
A: 可以通过以下方式:
- 捕获Ctrl+C信号,添加关闭钩子
- 提供退出菜单选项
- 使用System.exit(0)正常退出
Q6: 为什么我的运行效果与效果图不一致?
A: 可能的原因和解决方案:
- 中文乱码:确保控制台编码设置为UTF-8,Windows用户可以运行
chcp 65001 - 数据显示异常:检查数据库连接是否正常,是否有测试数据
- 菜单显示不全:确保控制台窗口足够大,建议最小80x25字符
- 启动失败:检查MySQL服务是否启动,数据库是否创建
- 格式错乱:确保使用等宽字体(如Consolas、Courier New)
Q7: 如何添加测试数据来获得更好的演示效果?
A: 可以通过以下方式添加测试数据:
- 在
StudentConsoleRunner中添加初始化数据的代码 - 使用SQL脚本在数据库中直接插入测试数据
- 通过应用程序的"添加学生"功能手动添加
- 建议添加10-20条不同专业、不同成绩的学生数据以获得最佳演示效果
📖 本篇小结
在上篇中,我们完成了:
✅ 环境搭建:Spring Boot 3.x + JPA + MySQL的完整配置
✅ 实体设计:学生实体类和数据库表映射
✅ 数据访问:Repository接口和自定义查询方法
✅ 业务逻辑:Service层的完整业务实现
✅ 控制台交互:用户友好的命令行界面
✅ 测试实践:单元测试和集成测试示例
🔗 相关文章
- 第三阶段:项目实战①小型项目开发(中篇) - 图书管理系统GUI版
- 第三阶段:项目实战①小型项目开发(下篇) - 项目运行与最佳实践
🎉 上篇完成!
你已经掌握了Spring Boot控制台应用开发的核心技能!
接下来让我们进入中篇,学习Swing GUI应用开发。
📖 继续阅读:中篇 - 图书管理系统GUI版
4万+

被折叠的 条评论
为什么被折叠?



