首页 > 编程语言 >24.Java程序员的经典错误

24.Java程序员的经典错误

时间:2023-01-03 18:25:31浏览次数:42  
标签:24 Java HashSet equals 程序员 ThreadLocal Objects 线程 使用

1. 使用Objects.equals比较对象

是JDK7提供的一种方法,可以快速实现对象的比较,有效避免烦人的空指针检查。但是这种方法很容易用错,例如:

1 Long longValue = 123L;
2 System.out.println(longValue==123); //true
3 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. 日期格式错误

请看下面的例子。

1 Instant instant = Instant.parse("2021-12-31T00:00:00.00Z");
2 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss")
3 .withZone(ZoneId.systemDefault());
4 System.out.println(formatter.format(instant));//2022-12-31 08:00:00

以上用于YYYY-MM-dd格式化, 年从2021变成了2022。为什么?这是因为javaDateTimeFormatter模式YYYYyyyy之间存在细微的差异。它们都代表一年,但是yyyy代表日历年,而YYYY代表星期。这是一个细微的差异,仅会导致一年左右的变更问题,因此您的代码本可以一直正常运行,而仅在新的一年中引发问题。12月31日按周计算的年份是2022年,正确的方式应该是使用yyyy-MM-dd格式化日期。

这个bug特别隐蔽

3. 在 ThreadPool 中使用 ThreadLocal

如果创建一个ThreadLocal变量,访问该变量的线程将创建一个线程局部变量。合理使用ThreadLocal可以避免线程安全问题。

但是,如果在线程池中使用ThreadLocal,就要小心了。您的代码可能会产生意想不到的结果。举个很简单的例子,假设我们有一个电商平台,用户购买商品后需要发邮件确认。

 1 private ThreadLocal<User> currentUser = ThreadLocal.withInitial(() -> null);
 2 
 3 private ExecutorService executorService = Executors.newFixedThreadPool(4);
 4 
 5 public void executor() {
 6     executorService.submit(()->{
 7         User user = currentUser.get();
 8         Integer userId = user.getId();
 9         sendEmail(userId);
10     });
11 }

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

4. 使用HashSet去除重复数据

在编码的时候,我们经常会有去重的需求。一想到去重,很多人首先想到的就是用HashSet去重。但是,不小心使用HashSet可能会导致去重失败。

1 User user1 = new User();
2 user1.setUsername("test");
3 
4 User user2 = new User();
5 user2.setUsername("test");
6 
7 List<User> users = Arrays.asList(user1, user2);
8 HashSet<User> sets = new HashSet<>(users);
9 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捕获异常。

 

标签:24,Java,HashSet,equals,程序员,ThreadLocal,Objects,线程,使用
From: https://www.cnblogs.com/midiyu/p/17023054.html

相关文章

  • 重学 Java 设计模式-结构型模式-适配器模式
    重学Java设计模式-结构型模式-适配器模式内容摘自:添加链接描述适配器模式介绍图片来自:https://refactoringguru.cn/design-patterns/adapter(opensnewwindow)适......
  • systemd自启动java程序
    一、背景条件1.Linux系统是Debian82.Java程序是test.jar,安装路径是/home/test/test.jar二、编写java的启动脚本startTest.sh#!/bin/shjava-jar/home/test/test......
  • error C2447: “{”: 缺少函数标题(是否是老式的形式表?)
    情况描述:这个错误在移植其它平台(Linux?)的代码到windows下,并使用较高版本的visualstudio时可能会出现错误原因:各平台下编码格式不同,在跨平台时代码可能需要切换到相......
  • 阮一峰JavaScript教程(上2)
    目录3.语法专题数据类型的转换概述强制转换Number()String()Boolean()自动转换自动转换为布尔值自动转换为字符串自动转换为数值参考链接错误处理机制Error实例对象原生错......
  • java集合Collection操作
    CollectionallCollections=newArrayList();//集合里添加数据allCollections.add("testName");//判断即合理是否包含某特定的数据if(al......
  • java国内地址三级、四级联动,附四级联动数据库
    因工作需要地址联动功能,使用java开发实体类importcom.baomidou.mybatisplus.annotation.TableName;importcom.baomidou.mybatisplus.annotation.TableName;importc......
  • NacosException: java.net.UnknownHostException: jmenv.tbsite.net
    第一次接触Nacos,启动时报错(java.net.UnknownHostException:jmenv.tbsite.net)解决方案。错误信息:Causedby:java.net.UnknownHostException:jmenv.tbsite.net......
  • Javascript条件语句
    Javascript条件语句之ifelse1<!DOCTYPEhtml>2<html>3<head>4<metacharset="utf-8">5<title>helloworld</title>6</head>......
  • JDK的版本有多少种,Java开发者应该选择哪一种?
    JDK的版本有多少种,Java开发者应该选择哪一种?先说结果,一般情况下,我们多数会选择OpenJDK或者AdoptOpenJDK的JDK实现,因为这是最精简最标准的版本,而且没有商业风险。另外,需要结......
  • 【独家揭秘】走进程序员的周末
    一个普通程序员,周末不出差、不加班、不出去玩的时候每天都在干什么呢?9点-12点昨晚失眠了,2点多才睡着,今天没有定闹铃,9:54自然醒一睁眼看见空调。突然想起来今天是周末了,强......