一、开篇
作为一名 Java 学习者,在综合项目开发的进程中,挑战与收获如影随形。每一次直面问题、攻克难题,都让我对 Java 知识体系的理解更上层楼。接下来,我将深度分享 Java 综合项目开发里的亲身经历,从棘手问题的遭遇,到课程练习项目的精细拆解,再到易混概念的抽丝剥茧、常见 Bug 的迎刃而解,以及掏心窝的学习心得,带您沉浸式走进 Java 项目开发的奇妙世界。
二、开发中遇到的问题及解决过程
(一)依赖冲突问题
问题场景
在搭建 Spring Boot + MyBatis Plus 项目时,引入 Log4j2、HikariCP 等多个第三方库。项目启动瞬间,控制台报错 ClassNotFoundException
,究其根源,是某个类在不同依赖包中版本打架,类加载器陷入 “选择困难”,无法正确加载类 。
解决步骤
- 定位依赖:使用 Maven 的
dependency:tree
命令(在项目根目录执行mvn dependency:tree
),查看项目的依赖树,找出冲突的依赖包。发现 Log4j2 的某个子依赖和 Spring Boot 自带的日志依赖版本不一致。 - 排除冲突:在
pom.xml
文件中,对冲突的依赖进行排除。例如,对于 Spring Boot 自带的日志依赖,在引入 Log4j2 相关依赖时,排除冲突的部分:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
- 验证测试:重新构建项目(执行
mvn clean install
),启动应用,查看日志是否正常输出,功能是否不受影响,确认依赖冲突问题解决。
总结
依赖冲突在多模块、多第三方库的项目中很常见。关键是要熟练使用工具(如 Maven 的依赖树命令 )定位问题,然后通过排除冲突依赖的方式解决。同时,要了解项目中各依赖的传递性,提前预防可能出现的冲突。
(二)数据库连接超时问题
问题场景
在项目的业务逻辑中,有一个批量插入数据到 MySQL 数据库的操作,数据量较大(约 10 万条 )。执行插入时,报错 java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms
,即数据库连接池获取连接超时。
解决步骤
- 分析配置:查看 HikariCP 连接池的配置,发现
maximumPoolSize
(最大连接数 )设置为 5,connectionTimeout
(连接超时时间 )为 30 秒。对于批量插入操作,连接数不足,导致后续请求等待超时。 - 调整参数:根据服务器性能和数据库承载能力,调整连接池参数。在 Spring Boot 项目的
application.yml
文件中修改:spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 60000 # 超时时间改为60秒
- 优化业务:除了调整连接池,还对批量插入操作进行优化。使用 MyBatis Plus 的
insertBatchSomeColumn
方法(需配置批量插入插件 ),减少数据库交互次数。同时,开启事务,保证数据一致性的同时,提升操作效率。 - 压力测试:使用 JMeter 对优化后的功能进行压力测试,模拟多线程并发插入数据,观察连接池的表现和数据库性能,确保调整后的参数和优化后的业务逻辑能稳定运行。
总结
数据库连接相关问题,要从连接池配置、业务逻辑优化两方面入手。合理设置连接池参数,结合业务场景优化数据库操作,能有效避免连接超时、性能瓶颈等问题。同时,压力测试是验证解决方案是否有效的重要手段。
三、课程练习项目拆解 - 基于 Java 的图书管理系统
(一)项目需求
实现一个简单的图书管理系统,包含图书的增删改查、用户的借阅与归还操作,以及基础的用户权限管理(分为普通用户和管理员 )。
(二)步骤拆解
1. 环境搭建与项目初始化
- 技术选型:使用 Spring Boot 框架快速搭建项目,数据库选择 MySQL,持久层框架用 MyBatis Plus,前端页面采用 Thymeleaf 模板引擎(也可结合 Vue 做前后端分离,这里为了练习传统模式选择 Thymeleaf )。
- 创建项目:通过 Spring Initializr 生成 Spring Boot 项目骨架,引入相关依赖(
spring-boot-starter-web
、spring-boot-starter-mybatis-plus
、mysql-connector-java
等 )。 - 配置数据库:在
application.yml
中配置数据库连接信息:spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/book_manage?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456
2. 实体类与数据库表设计
- 图书实体(Book):
@Data @TableName("book") public class Book { @TableId(type = IdType.AUTO) private Long id; private String bookName; private String author; private Integer stock; // 库存数量 private Integer borrowCount; // 被借阅次数 }
对应数据库表
book
,包含id
(主键自增 )、book_name
、author
、stock
、borrow_count
字段。 - 用户实体(User):
@Data @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String username; private String password; private Integer role; // 0-普通用户,1-管理员 }
对应数据库表
user
,包含id
、username
、password
、role
字段。 - 借阅记录实体(BorrowRecord):
@Data @TableName("borrow_record") public class BorrowRecord { @TableId(type = IdType.AUTO) private Long id; private Long userId; private Long bookId; private LocalDateTime borrowTime; private LocalDateTime returnTime; private Integer status; // 0-借阅中,1-已归还 }
对应数据库表
borrow_record
,记录用户借阅图书的信息。
3. 持久层开发(MyBatis Plus 实现)
- BookMapper:
public interface BookMapper extends BaseMapper<Book> { // 可自定义复杂查询方法,如根据书名模糊查询 List<Book> selectByBookName(String bookName); }
在对应的 XML 映射文件中编写 SQL:
<select id="selectByBookName" resultType="com.example.bookmanage.entity.Book"> select * from book where book_name like concat('%',#{bookName},'%') </select>
- UserMapper 和 BorrowRecordMapper 类似,继承
BaseMapper
,根据业务需求自定义方法。
4. 业务层(Service)实现
- BookService:
@Service public class BookService { @Resource private BookMapper bookMapper; public List<Book> listAllBooks() { return bookMapper.selectList(null); } public boolean addBook(Book book) { return bookMapper.insert(book) > 0; } public List<Book> searchBooksByName(String bookName) { return bookMapper.selectByBookName(bookName); } // 其他业务方法,如修改图书信息、减少库存(借阅时)等 }
- UserService:处理用户登录、权限判断等逻辑;BorrowRecordService:处理借阅、归还流程,更新图书库存和借阅记录状态。
5. 控制层(Controller)与页面交互
- BookController:
@Controller @RequestMapping("/book") public class BookController { @Resource private BookService bookService; @GetMapping("/list") public String listBooks(Model model) { List<Book> bookList = bookService.listAllBooks(); model.addAttribute("books", bookList); return "book/list"; // 跳转到图书列表页面 } @GetMapping("/add") public String addBookPage() { return "book/add"; // 跳转到添加图书页面 } @PostMapping("/add") public String addBook(@ModelAttribute Book book) { bookService.addBook(book); return "redirect:/book/list"; // 添加后重定向到列表页 } }
- 前端页面使用 Thymeleaf 语法,展示图书列表、表单提交等,实现与用户的交互。
6. 权限管理与拦截器
实现 HandlerInterceptor:
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
// 排除登录、注册等公共接口
if (requestURI.contains("/login") || requestURI.contains("/register")) {
return true;
}
// 获取用户登录状态(可通过 Session 或 Token,这里用 Session 示例)
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
response.sendRedirect("/user/login");
return false;
}
// 管理员权限判断,如访问 /book/add 需管理员权限
if (requestURI.contains("/book/add") && user.getRole() != 1) {
response.sendRedirect("/book/list");
return false;
}
return true;
}
}
- 配置拦截器:在 Spring Boot 配置类中注册拦截器:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns("/**"); } }
7. 功能测试与优化
- 使用 Postman 测试接口,模拟不同用户(普通用户、管理员 )执行增删改查、借阅归还操作,检查功能是否正常。
- 对页面进行美化,优化用户体验;对数据库查询进行性能分析,添加索引(如在
book
表的book_name
字段添加索引,优化模糊查询 )。
四、Java 中易混淆概念解析
(一)重载(Overload)与重写(Override)
1. 重载(Overload)
- 定义:在同一个类中,方法名相同,参数列表(参数个数、类型、顺序 )不同的方法之间构成重载。
- 示例:
public class MathUtils { public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } }
- 特点:与返回值类型无关,编译器在编译阶段根据参数列表区分不同的方法。主要用于实现类似功能但参数不同的场景,提升代码可读性和复用性。
2. 重写(Override)
- 定义:子类继承父类后,对父类中已有的方法(非私有、非静态、非 final )进行重新实现,方法名、参数列表、返回值类型(子类重写方法的返回值类型需是父类方法返回值类型的子类型或相同 )必须相同。
- 示例:
public class Animal { public void speak() { System.out.println("动物发出声音"); } } public class Dog extends Animal { @Override public void speak() { System.out.println("小狗汪汪叫"); } }
- 特点:发生在父类和子类之间,运行时根据对象的实际类型动态绑定方法(多态性 )。用于子类根据自身特性,定制父类方法的实现逻辑。
(二)ArrayList 与 LinkedList
1. 数据结构
- ArrayList:基于动态数组实现,底层是一个
Object[]
数组。 - LinkedList:基于双向链表实现,每个节点包含数据、前驱节点和后继节点的引用。
2. 操作性能
- 查询:ArrayList 支持随机访问,通过索引
get(i)
的时间复杂度是O(1)
;LinkedList 需从链表头或尾开始遍历,时间复杂度是O(n)
,查询效率低。 - 增删:ArrayList 在中间插入或删除元素时,需要移动后续元素,时间复杂度
O(n)
;LinkedList 在中间插入或删除元素,只需修改节点的引用,时间复杂度O(1)
(找到对应节点后 ),增删效率高(但查找节点仍需O(n)
时间 )。
3. 内存占用
- ArrayList 因数组存储,内存连续,有一定的空间预分配和扩容机制,可能存在内存浪费;LinkedList 每个节点存储额外的前后节点引用,内存开销相对较大。
4. 使用场景
- 频繁查询、遍历数据,用 ArrayList;频繁在中间进行增删操作,用 LinkedList(但实际开发中,若数据量不大,ArrayList 整体表现更优,因为 LinkedList 的查找开销可能抵消增删优势 )。
五、Java 学习心得或经验分享
(一)扎实基础,重视原理
Java 基础语法(面向对象、异常处理、集合框架 )是学习框架、做项目的 “地基”。要深究原理,像 ArrayList 扩容机制、多线程同步原理等。可通过阅读 JDK 源码、调试源码,挖掘底层逻辑,知其然更知其所以然 。
(二)项目驱动,实践为王
学 Java 切勿 “纸上谈兵”,多做项目是 “捷径”。从简单的图书管理系统、博客系统,到复杂的电商、社交项目,在实践中暴露问题、积累经验。遇到难题别慌,拆解问题、逐个击破,复盘时总结方法,能力自然 “水涨船高” 。
(三)拥抱框架,理解思想
Spring Boot、Spring Cloud 等框架是 Java 开发 “利器”,要深入学习。但别止步于 “会用”,要探究框架设计思想,如依赖注入(DI )、面向切面编程(AOP )。明白框架如何简化开发、解耦业务,才能在项目中灵活运用,甚至自定义扩展 。