5 errores que comete el 90% de los desarrolladores de Java

Si este artículo es útil para usted, salve al niño, puede votar mucho , el canal de votación  , muchas gracias ~~

prefacio

Como programador de desarrollo de Java, no sé si te has encontrado con algunos errores increíbles. Estos errores suelen tardar horas en resolverse. Cuando los encuentres, puedes llamarte tonto a ti mismo en silencio. Sí, estos errores ridículos son básicamente causados ​​por ignorar algunos conocimientos básicos. De hecho, todos son errores de muy bajo nivel. Hoy, resumo algunos errores de codificación comunes y luego brindo soluciones. Espero que pueda evitar tales problemas en su codificación diaria.

1. Usar Objects.equals para comparar objetos

Se cree que este método es familiar para todos, e incluso muchas personas lo usan a menudo. Es un método provisto por JDK7, que puede realizar rápidamente la comparación de objetos y evitar de manera efectiva las molestas verificaciones de punteros nulos. Pero este método es fácil de usar incorrectamente, por ejemplo:

Long longValue = 123L;
System.out.println(longValue==123); //true
System.out.println(Objects.equals(longValue,123)); //false
复制代码

¿Por qué reemplazar ==con da Objects.equals()como resultado un resultado diferente? Esto se debe a que al usar el ==compilador obtendrá el tipo de datos básico correspondiente al tipo de paquete longValuey luego lo comparará con este tipo de datos básico, lo que significa que el compilador convertirá automáticamente la constante al tipo de datos básico de comparación en lugar del tipo de paquete.

Después de usar este Objects.equals()método, el tipo de datos básico de la constante predeterminada del compilador es int. El siguiente es el código fuente Objects.equals(), que a.equals(b)usa el Long.equals()tipo de objeto para ser juzgado, porque el compilador ya ha considerado que la constante es un inttipo, por lo que el resultado de la comparación debe ser 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;
}
复制代码

Conociendo el motivo, la solución es sencilla. Declare el tipo de datos de la constante directamente, por ejemplo, Objects.equals(longValue,123L). De hecho, si la lógica es estricta, los problemas anteriores no surgirán. Lo que tenemos que hacer es mantener buenos hábitos de codificación.

2. Formato de fecha incorrecto

En nuestro desarrollo diario, a menudo necesitamos formatear la fecha, pero muchas personas usan el formato incorrecto, lo que genera situaciones inesperadas. Por favor, vea el ejemplo a continuación.

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。为什么?这是因为 javaDateTimeFormatter 模式YYYYyyyy之间存在细微的差异。它们都代表一年,但是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
复制代码

Los lectores cuidadosos ya deberían haber adivinado el motivo del fracaso. HashSetUse hashcodepara abordar la tabla hash, use el equalsmétodo para determinar si los objetos son iguales. Si el objeto personalizado no tiene un método anulado y es igual al método, los métodos y los métodos hashcodedel objeto principal se usan de manera predeterminada . Entonces se considerará que estos son dos objetos diferentes, por lo que la deduplicación falla.hashcodeequalsHashSet

5. Se come la excepción en el grupo de subprocesos

ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(()->{
    //do something
    double result = 10/0;
});
复制代码

El código anterior simula un escenario en el que un grupo de subprocesos genera una excepción. Nuestro código comercial real tiene que lidiar con varias situaciones posibles, por lo que es muy probable que se active por algunas razones específicas RuntimeException.

Pero si no hay un manejo especial, esta excepción será absorbida por el grupo de subprocesos. Esto conducirá a problemas que ni siquiera conoces, lo cual es una consecuencia muy grave. try catchPor lo tanto, es mejor detectar excepciones en el grupo de subprocesos .

Resumir

Este artículo resume 5 errores que son fáciles de cometer durante el proceso de desarrollo. Espero que todos puedan desarrollar buenos hábitos de codificación.

Si este artículo es útil para usted, salve al niño, puede votar mucho , el canal de votación  , muchas gracias ~~

Supongo que te gusta

Origin juejin.im/post/7182184496517611576
Recomendado
Clasificación