If this article is helpful to you, please save the child, you can vote a lot , the voting channel , thank you very much~~
foreword
As a java development programmer, I don't know if you have encountered some incredible bugs. These errors usually take you hours to resolve. When you find them, you may silently call yourself a fool. Yes, these ridiculous bugs are basically caused by you ignoring some basic knowledge. In fact, they are all very low-level mistakes. Today, I summarize some common coding mistakes, and then give solutions. I hope you can avoid such problems in your daily coding.
1. Using Objects.equals to compare objects
This method is believed to be familiar to everyone, and even many people often use it. It is a method provided by JDK7, which can quickly realize the comparison of objects and effectively avoid annoying null pointer checks. But this method is easy to use wrongly, for example:
Long longValue = 123L;
System.out.println(longValue==123); //true
System.out.println(Objects.equals(longValue,123)); //false
复制代码
Why does replacing ==
with Objects.equals()
result in a different result? This is because using the ==
compiler will get the basic data type corresponding to the package type longValue
, and then compare with this basic data type, which means that the compiler will automatically convert the constant to the comparison basic data type instead of the package type.
After using this Objects.equals()
method, the basic data type of the compiler default constant is int
. The following is the source code Objects.equals()
, which a.equals(b)
uses the Long.equals()
object type to be judged, because the compiler has already considered the constant to be a int
type, so the comparison result must be 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;
}
复制代码
Knowing the reason, the solution is simple. Directly declare the data type of the constant, eg Objects.equals(longValue,123L)
. In fact, if the logic is strict, the above problems will not arise. What we need to do is to maintain good coding habits.
2. Wrong date format
In our daily development, we often need to format the date, but many people use the wrong format, which leads to unexpected situations. Please see the example below.
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
复制代码
Careful readers should have already guessed the reason for the failure. HashSet
Use hashcode
to address the hash table, use the equals
method to determine whether the objects are equal. If the custom object has no overridden method and equals method, the methods and methods hashcode
of the parent object are used by default . So it will be considered that these are two different objects, so the deduplication fails.hashcode
equals
HashSet
5. The exception in the thread pool is eaten
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(()->{
//do something
double result = 10/0;
});
复制代码
The above code simulates a scenario where a thread pool throws an exception. Our real business code has to deal with various possible situations, so it is very likely to be triggered for some specific reasons RuntimeException
.
But if there is no special handling, this exception will be eaten by the thread pool. This will lead to problems that you don't even know about, which is a very serious consequence. try catch
Therefore, it is better to catch exceptions in the thread pool .
Summarize
This article summarizes 5 mistakes that are easy to make during the development process. I hope everyone can develop good coding habits.
If this article is helpful to you, please save the child, you can vote a lot , the voting channel , thank you very much~~