假如本文对你有协助的话,救救孩子吧,能够投很多票,投票通道,万分感谢呀~~
前言
作为一名java开发程序员,不知道咱们有没有遇到过一些匪夷所思的bug。这些过错一般需求您几个小时才能处理。当你找到它们的时分,你可能会默默地骂自己是个傻瓜。是的,这些可笑的bug基本上都是你疏忽了一些基础知识造成的。其实都是很低级的过错。今日,我总结一些常见的编码过错,然后给出处理方案。期望咱们在日常编码中能够防止这样的问题。
1. 运用Objects.equals比较目标
这种办法相信咱们并不陌生,甚至很多人都常常运用。是JDK7提供的一种办法,能够快速实现目标的比较,有用防止烦人的空指针查看。可是这种办法很容易用错,例如:
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
。
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)
。其实假如逻辑严密,就不会呈现上面的问题。咱们需求做的是保持良好的编码习气。
2. 日期格局过错
在咱们日常的开发中,常常需求对日期进行格局化,可是很多人运用的格局不对,导致呈现意想不到的状况。请看下面的比如。
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造成了生产事故。
3. 在 ThreadPool 中运用 ThreadLocal
假如创立一个ThreadLocal
变量,拜访该变量的线程将创立一个线程局部变量。合理运用ThreadLocal
能够防止线程安全问题。
可是,假如在线程池中运用ThreadLocal
,就要当心了。您的代码可能会发生意想不到的成果。举个很简单的比如,假定咱们有一个电商渠道,用户购买产品后需求发邮件承认。
private ThreadLocal<User> 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
获取用户信息的时分,很可能会误获取到他人的信息。您能够运用会话来处理这个问题。
4. 运用HashSet去除重复数据
在编码的时分,咱们常常会有去重的需求。一想到去重,很多人首先想到的便是用HashSet
去重。可是,不当心运用 HashSet
可能会导致去重失败。
User user1 = new User();
user1.setUsername("test");
User user2 = new User();
user2.setUsername("test");
List<User> users = Arrays.asList(user1, user2);
HashSet<User> sets = new HashSet<>(users);
System.out.println(sets.size());// the size is 2
细心的读者应该现已猜到失败的原因了。HashSet
运用hashcode
对哈希表进行寻址,运用equals
办法判断目标是否相等。假如自定义目标没有重写hashcode
办法和equals办法,则默许运用父目标的hashcode
办法和equals
办法。所以HashSet
会以为这是两个不同的目标,所以导致去重失败。
5. 线程池中的反常被吃掉
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(()->{
//do something
double result = 10/0;
});
上面的代码模拟了一个线程池抛出反常的场景。咱们真实的事务代码要处理各种可能呈现的状况,所以很有可能由于某些特定的原因而触发RuntimeException
。
可是假如没有特别处理,这个反常就会被线程池吃掉。这样就会导出呈现问题你都不知道,这是很严重的后果。因而,最好在线程池中try catch
捕获反常。
总结
本文总结了在开发过程中很容易犯的5个过错,期望咱们养成良好的编码习气。
假如本文对你有协助的话,救救孩子吧,能够投很多票,投票通道,万分感谢呀~~