Java 开发的编程噩梦,这些坑你没踩过算我输

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Java 开发的编程噩梦,这些坑你没踩过算我输

随处可见的 Null 值

我见过很多的代码会把 Null 值作为返回值,当你预期是一个字符串时,意外得到了一个 Null 值;当你预期得到一个 List 时,意外又得到了一个 Null 值,如果你不进行处理,那么你还会意外得到 NullPointerException. 就像下面这样。


// 情况1
String userTag = getUserTag();
if (userTag.equals("admin")) { // NullPointerException
   // ...
}
// 情况2
List<String> carList = getCarList();
for (String car : carList) { // NullPointerException
    // ...
}

为了防止这种情况,你可以在 List 返回时给出一个空的集合而不是 Null,如果是字符串,你可以把要确定有值对象放在比较的前面。


if ("admin".equals(userTag)) {
    // ...
}
// 或者
if (Objects.equals(userTag,"admin")){
    // ...
}

没有进行空值检查

可能你考虑到了上面的 Null 值情况,但是在实际处理时没有考虑空值情况,比如字符空串空串 "",或者集合为空。那么在后续处理时又有可能得到一个 NullPointerException. 所以你应该进行空值判断。


String userTag = getUserTag();
if (userTag != null && userTag.trim() != "") {
    // ...
}
List<String> carList = getCarList();
if (carList != null && !carList.isEmpty()) {
    // ...
}

忽略的异常处理

异常处理总是一件烦人的事,而忽略异常似乎总有一种吸引人的魔力。我见过像下面这样的代码。


try {
    List<String> result= request();
    // ...
}catch (Exception e){
}

你没有看错,catch 中没有任何内容,后来出现了问题,看着日志文件一片太平无迹可寻。异常是故意抛出来的,你应该正确处理它们或者继续抛出。而且同时,你该输出一行日志用来记录这个异常,方便以后的问题追踪。

没有释放资源

在读取文件或者请求网络资源时,总是需要进行 close 操作,这很重要,否则可能会阻塞其他线程的使用。但是初学者可能会忘记这一步操作。其实在 Java 7 开始,就提供了 try-with-resources 自动关闭资源的特性,只需要把打开的资源放入 try 中。


try (FileReader fileReader = new FileReader("setting.xml")) {
    // fileReader.read();
    // ...
} catch (Exception e) {
    e.printStackTrace();
}

像上面这样,不需要在 finally 里手动调用 fileReaderclose 方法关闭资源,因为放在 try 里的资源调用会在使用完毕时自动调用 close. 而且不管是否有异常抛出,这很实用。

ConcuretModificationException

总有一天你会遇到 ConcuretModificationException ,然后开始百度搜索它的解决方式,这个异常最常见的场景是你在遍历一个集合时进行更新操作,比如像下面这样。


List<String> list = new ArrayList<>();
list.add("a1");
list.add("b1");
list.add("b2");
list.add("c1");
for (String s : list) {
    if ("b1".equals(s)) {
        list.remove(s);
    }
}

这个异常很有用处,因为 ArrayList 不是线程安全的集合,假设你这边一边遍历,另一个线程不断更新,非线程安全集合会导致你的遍历结果不正确,所以这个异常的存在是合理的。同理 HashMap 也是如此,关于 HashMap 之前已经有一篇文章详细介绍了,可以参考 最通俗易懂的 HashMap 源码分析解读

缺少注释

准确的注释可以救人于水火,这点有时候一点也不夸张。虽然说优秀的代码本身就是非常好的注释,但是这实际开发起来,很少发生。注释并不需要你事无巨细的一一记录,但是你该在核心逻辑添加应有的注释,比如复杂逻辑的实现思路,当前逻辑业务需求。某个判断的添加原因,某个异常的发生情况等等。这可以让你在未来的某一天需要回看现在的代码时感谢自己。更可以让你在某天的甩锅中轻松胜出

不进行代码测试

我见过有些同事在功能开发完毕后直接扔给对接同事使用,而自己却没有经过任何测试,或者只是测试了某个简单的情况。测试是开发过程中的重要环节,没有经过严格测试的代码很难说没有问题,我觉得在功能开发完毕后至少需要单元测试特殊用例测试,集成测试以及其他形式的测试。严格的测试不仅可以第一时间发现问题,更可以减少后面不必要的对接调试时间

重复造轮子

你知道的,Java 社区非常活跃,存在着大量的第三方类库,开源作者可能花费了数年时间去维护和完善类库,这些类库非常优秀。同时 JDK 也提供了大量的常用的功能封装。这些都可以为我们的开发速度插上翅膀。所以,当你需要一个功能时候,应该首先看下 JDK 和已经引入的类库中是否已经存在相同功能,而不是自己重复造轮子,而且大部分情况下你造的轮子还不如别人好。

下面举些例子。

  • 你需要日志记录,可以使用 logback.
  • 你需要网络操作,可以使用 netty.
  • 你需要解析 JSON,可以使用 gson.
  • 你需要解析表格,可以使用 apache poi.
  • 你需要通用操作,可以使用 apache commons.

另外一种情况是,你可能不知道某个功能在 JDK 中已经实现,这时候你应该多多查看 JDK Document. 我就在工作中见到过同事手写字符串 split,为了获取时间戳把 Date 对象转换到 Calendar.

缺少必要的沟通

这个部分是和开发没有关系的,但是这个环节往往会影响最终的开发结果。进行具体的开发之前,你应该详细的沟通并理解功能的需求,这样你才能针对具体的需求写出不偏离实际需要的代码。有时候你很有可能因为缺少必要的沟通,错误了理解了需求,最终在开发完毕后发现自己写的功能完全没有用处。

没有代码规范

代码规范性非常重要,如果一个项目里充斥着各种稀奇古怪的代码规范,会让维护者十分头疼。而且软件行业高速发展,对开发者的综合素质要求也越来越高,优秀的编程习惯也可以提高软件的最终质量。比如:标新立异的命名风格挑战阅读习惯;五花八门的错误码人为地 增加排查问题的难度;工程结构混 乱导致后续项目维护艰难;没有鉴权的漏洞代码易被黑客攻击;质量低下的代码上线之后漏洞百出等等。因为没有统一的代码规范,开发中的问题可能层出不穷。

下面简单列举些应该统一的开发规范。如命名风格如何是好;常量名称结构怎样;代码格式怎么统一;日期时间格式如何处理;集合处理注意事项;日志打印有无规范;前后交互具体规约等。

上面所说的开发规范代码规范推荐阿里推出的 《Java 开发手册》,里面详细列举了在 Java 开发中各个方面应该遵守的规约和规范。最新版本在 8月 3日已经发布,可以在公众号 “未读代码” 直接回复 "java" 获取最新版 pdf.

总结

Bug 和技术上的误解都是美丽的谜团,福尔摩斯般的我们终将解决这些问题。命运自己掌握,每一次探清的这些技术误解,都会增加我们对开发编码的理解。尽情接招吧,色彩斑斓才有趣,万般体验才是人生,不管是多样的技术,还是多样的问题,我都想看见。

参考:

[1] A beginner’s guide to Java programming nightmares

[2] Java™ Platform, Standard Edition 8 API Specification

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
20天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
1月前
|
监控 Java API
如何使用Java语言快速开发一套智慧工地系统
使用Java开发智慧工地系统,采用Spring Cloud微服务架构和前后端分离设计,结合MySQL、MongoDB数据库及RESTful API,集成人脸识别、视频监控、设备与环境监测等功能模块,运用Spark/Flink处理大数据,ECharts/AntV G2实现数据可视化,确保系统安全与性能,采用敏捷开发模式,提供详尽文档与用户培训,支持云部署与容器化管理,快速构建高效、灵活的智慧工地解决方案。
|
4天前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
31 12
|
22天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
40 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
9天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
60 13
|
14天前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
48 10
|
7天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
45 2
|
23天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
23天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
17天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####