90%的Java开发人员都会犯的5个错误

简介: 作为一名java开发程序员,不知道大家有没有遇到过一些匪夷所思的bug。这些错误通常需要您几个小时才能解决。当你找到它们的时候,你可能会默默地骂自己是个傻瓜。是的,这些可笑的bug基本上都是你忽略了一些基础知识造成的。其实都是很低级的错误。今天,我总结一些常见的编码错误,然后给出解决方案。希望大家在日常编码中能够避免这样的问题。

前言
作为一名java开发程序员,不知道大家有没有遇到过一些匪夷所思的bug。这些错误通常需要您几个小时才能解决。当你找到它们的时候,你可能会默默地骂自己是个傻瓜。是的,这些可笑的bug基本上都是你忽略了一些基础知识造成的。其实都是很低级的错误。今天,我总结一些常见的编码错误,然后给出解决方案。希望大家在日常编码中能够避免这样的问题。

  1. 使用Objects.equals比较对象
    这种方法相信大家并不陌生,甚至很多人都经常使用。是JDK7提供的一种方法,可以快速实现对象的比较,有效避免烦人的空指针检查。但是这种方法很容易用错,例如:
    ini复制代码Long longValue = 123L;
    System.out.println(longValue==123); //true
    System.out.println(Objects.equals(longValue,123)); //false

为什么替换==为Objects.equals()会导致不同的结果?这是因为使用==编译器会得到封装类型对应的基本数据类型longValue,然后与这个基本数据类型进行比较,相当于编译器会自动将常量转换为比较基本数据类型, 而不是包装类型。
使用该Objects.equals()方法后,编译器默认常量的基本数据类型为int。下面是源码Objects.equals(),其中a.equals(b)使用的是Long.equals()会判断对象类型,因为编译器已经认为常量是int类型,所以比较结果一定是false。
typescript复制代码public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}

public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}

知道了原因,解决方法就很简单了。直接声明常量的数据类型,如Objects.equals(longValue,123L)。其实如果逻辑严密,就不会出现上面的问题。我们需要做的是保持良好的编码习惯。

  1. 日期格式错误
    在我们日常的开发中,经常需要对日期进行格式化,但是很多人使用的格式不对,导致出现意想不到的情况。请看下面的例子。
    ini复制代码Instant instant = Instant.parse("2021-12-31T00:00:00.00Z");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss")
    .withZone(ZoneId.systemDefault());
    System.out.println(formatter.format(instant));//2022-12-31 08:00:00

以上用于YYYY-MM-dd格式化, 年从2021 变成了 2022。为什么?这是因为 java 的DateTimeFormatter 模式YYYY和yyyy之间存在细微的差异。它们都代表一年,但是yyyy代表日历年,而YYYY代表星期。这是一个细微的差异,仅会导致一年左右的变更问题,因此您的代码本可以一直正常运行,而仅在新的一年中引发问题。12月31日按周计算的年份是2022年,正确的方式应该是使用yyyy-MM-dd格式化日期。
这个bug特别隐蔽。这在平时不会有问题。它只会在新的一年到来时触发。我公司就因为这个bug造成了生产事故。

  1. 在 ThreadPool 中使用 ThreadLocal
    如果创建一个ThreadLocal 变量,访问该变量的线程将创建一个线程局部变量。合理使用ThreadLocal可以避免线程安全问题。
    但是,如果在线程池中使用ThreadLocal ,就要小心了。您的代码可能会产生意想不到的结果。举个很简单的例子,假设我们有一个电商平台,用户购买商品后需要发邮件确认。
    ini复制代码private ThreadLocal currentUser = ThreadLocal.withInitial(() -> null);

private ExecutorService executorService = Executors.newFixedThreadPool(4);

public void executor() {
executorService.submit(()->{
User user = currentUser.get();
Integer userId = user.getId();
sendEmail(userId);
});
}

如果我们使用ThreadLocal来保存用户信息,这里就会有一个隐藏的bug。因为使用了线程池,线程是可以复用的,所以在使用ThreadLocal获取用户信息的时候,很可能会误获取到别人的信息。您可以使用会话来解决这个问题。

  1. 使用HashSet去除重复数据
    在编码的时候,我们经常会有去重的需求。一想到去重,很多人首先想到的就是用HashSet去重。但是,不小心使用 HashSet 可能会导致去重失败。
    ini复制代码User user1 = new User();
    user1.setUsername("test");

User user2 = new User();
user2.setUsername("test");

List users = Arrays.asList(user1, user2);
HashSet sets = new HashSet<>(users);
System.out.println(sets.size());// the size is 2

细心的读者应该已经猜到失败的原因了。HashSet使用hashcode对哈希表进行寻址,使用equals方法判断对象是否相等。如果自定义对象没有重写hashcode方法和equals方法,则默认使用父对象的hashcode方法和equals方法。所以HashSet会认为这是两个不同的对象,所以导致去重失败。

  1. 线程池中的异常被吃掉
    ini复制代码ExecutorService executorService = Executors.newFixedThreadPool(1);
    executorService.submit(()->{
    //do something
    double result = 10/0;
    });

上面的代码模拟了一个线程池抛出异常的场景。我们真正的业务代码要处理各种可能出现的情况,所以很有可能因为某些特定的原因而触发RuntimeException 。
但是如果没有特殊处理,这个异常就会被线程池吃掉。这样就会导出出现问题你都不知道,这是很严重的后果。因此,最好在线程池中try catch捕获异常。
总结
本文总结了在开发过程中很容易犯的5个错误,希望大家养成良好的编码习惯。

相关文章
|
3月前
|
NoSQL Java 程序员
阿里开发人员献礼“Java架构成长笔记”,深入内核,拒绝蒙圈
提起阿里,行外人联想到的关键词无非是“交易”、“淘宝”、“支付宝”,但对于程序员来说,阿里庞大的技术体系才是最吸引人的。实际上阿里作为国内一线互联网公司的头把交椅,内部的技术体系和发展都是备受关注的,对于程序员来说,能够进到阿里工作,就是对自己的技术水平进行一个提升和学习。
阿里开发人员献礼“Java架构成长笔记”,深入内核,拒绝蒙圈
阿里开发人员献礼“Java架构成长笔记”,深入内核,拒绝蒙圈
提起阿里,行外人联想到的关键词无非是“交易”、“淘宝”、“支付宝”,但对于程序员来说,阿里庞大的技术体系才是最吸引人的。实际上阿里作为国内一线互联网公司的头把交椅,内部的技术体系和发展都是备受关注的,对于程序员来说,能够进到阿里工作,就是对自己的技术水平进行一个提升和学习。 实际上,阿里内部的技术交流氛围是极其强烈的,技术人员也经常会交流自己的学习经验和技术总结。今天要分享的,则是Alibaba开发人员献礼的“Java架构成长笔记”,带我们深入内核,拒绝蒙圈!
|
消息中间件 存储 NoSQL
为什么Java开发人员在简历上不敢轻易写精通Java
为什么Java开发人员在简历上不敢轻易写精通Java
157 0
为什么Java开发人员在简历上不敢轻易写精通Java
|
XML 安全 Java
帮助 Java 开发人员进行 Bean 映射的 8 大框架
帮助 Java 开发人员进行 Bean 映射的 8 大框架
262 0
帮助 Java 开发人员进行 Bean 映射的 8 大框架
|
设计模式 Java 数据库连接
成为一名优秀 Java 开发人员的 7 个步骤
如果你是一名对技术充满热情的 Java 开发人员,那么本文列举的这 7 个步骤能让你成为一名优秀的 Java 工程师。
104 0
|
IDE Java Devops
2020年每个Java开发人员都应该知道的10个基本工具
2020年每个Java开发人员都应该知道的10个基本工具
100 0
2020年每个Java开发人员都应该知道的10个基本工具
|
IDE Java Devops
2020年每个Java开发人员都应该知道的10个基本工具
2020年每个Java开发人员都应该知道的10个基本工具
116 0
2020年每个Java开发人员都应该知道的10个基本工具
|
JavaScript IDE Java
PHP开发人员对JAVA的WEB开发入门(初版-基础知识)
最近准备对其他部门PHP开发的童鞋做一个对JAVA的培训。知己知彼,百战不殆,我要先了解点PHP,才能确认他们的基础,达到好的授课效果。 PHP(原始为Personal Home Page的缩写,后正式更名为Hypertext Preprocessor超文本预处理器)是一种通用开源脚本语言。语法吸收了C语言,Java和Perl的特点,利于学习,适用于Web开发领域。
PHP开发人员对JAVA的WEB开发入门(初版-基础知识)
|
Oracle IDE 前端开发
Java 8 失宠!开发人员向 Java 11 转移...
Java 8 失宠!开发人员向 Java 11 转移...
130 0
Java 8 失宠!开发人员向 Java 11 转移...
|
敏捷开发 架构师 Java
各种 Java Web 开发人员的通用工具
本文主要介绍Java程序员应该在2019年学习的一些基本和高级工具。如果你是一位经验丰富的Java开发人员,你可能对这些工具很熟悉,但如果不是,现在就是是开始学习这些工具的好时机。