JavaRush /Blog Java /Random-FR /Pause café #146. 5 erreurs que commettent 99% des dévelop...

Pause café #146. 5 erreurs que commettent 99% des développeurs Java. Chaînes en Java - vue intérieure

Publié dans le groupe Random-FR

5 erreurs commises par 99% des développeurs Java

Source : Medium Dans cet article, vous découvrirez les erreurs les plus courantes commises par de nombreux développeurs Java. Pause café #146.  5 erreurs que commettent 99% des développeurs Java.  Chaînes en Java - vue intérieure - 1En tant que programmeur Java, je sais à quel point il est mauvais de passer beaucoup de temps à corriger des bogues dans votre code. Parfois, cela prend plusieurs heures. Cependant, de nombreuses erreurs apparaissent du fait que le développeur ignore les règles de base, c'est-à-dire qu'il s'agit d'erreurs de très bas niveau. Aujourd'hui, nous allons examiner quelques erreurs de codage courantes, puis expliquer comment les corriger. J'espère que cela vous aidera à éviter des problèmes dans votre travail quotidien.

Comparaison d'objets à l'aide d'Objects.equals

Je suppose que vous connaissez cette méthode. De nombreux développeurs l'utilisent fréquemment. Cette technique, introduite dans JDK 7, vous aide à comparer rapidement des objets et à éviter efficacement les vérifications ennuyeuses des pointeurs nuls. Mais cette méthode est parfois mal utilisée. Voici ce que je veux dire :
Long longValue = 123L;
System.out.println(longValue==123); //true
System.out.println(Objects.equals(longValue,123)); //false
Pourquoi remplacer == par Objects.equals() produirait-il un mauvais résultat ? En effet, le compilateur == obtiendra le type de données sous-jacent correspondant au type d'empaquetage longValue , puis le comparera à ce type de données sous-jacent. Cela équivaut à ce que le compilateur convertisse automatiquement les constantes en type de données de comparaison sous-jacent. Après avoir utilisé la méthode Objects.equals() , le type de données de base par défaut de la constante du compilateur est int . Vous trouverez ci-dessous le code source de Objects.equals()a.equals(b) utilise Long.equals() et détermine le type de l'objet. Cela se produit parce que le compilateur a supposé que la constante était de type int , le résultat de la comparaison doit donc être faux.
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;
    }
Connaissant la raison, corriger l'erreur est très simple. Déclarez simplement le type de données des constantes, comme Objects.equals(longValue,123L) . Les problèmes ci-dessus ne se poseront pas si la logique est stricte. Ce que nous devons faire, c'est suivre des règles de programmation claires.

Format de date incorrect

Dans le développement quotidien, vous devez souvent modifier la date, mais de nombreuses personnes utilisent le mauvais format, ce qui conduit à des choses inattendues. Voici un exemple :
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
Celui-ci utilise le format AAAA-MM-jj pour changer la date de 2021 à 2022. Tu ne devrais pas faire ça. Pourquoi? En effet, le modèle Java DateTimeFormatter « YYYY » est basé sur la norme ISO-8601, qui définit l'année comme le jeudi de chaque semaine. Mais le 31 décembre 2021 tombant un vendredi, le programme indique à tort 2022. Pour éviter cela, vous devez utiliser le format aaaa-MM-jj pour formater la date . Cette erreur se produit rarement, seulement avec l'arrivée de la nouvelle année. Mais dans mon entreprise, cela a provoqué un échec de production.

Utilisation de ThreadLocal dans ThreadPool

Si vous créez une variable ThreadLocal , alors un thread accédant à cette variable créera une variable locale de thread. De cette façon, vous pouvez éviter les problèmes de sécurité des threads. Cependant, si vous utilisez ThreadLocal sur un pool de threads , vous devez être prudent. Votre code peut produire des résultats inattendus. Pour un exemple simple, disons que nous avons une plate-forme de commerce électronique et que les utilisateurs doivent envoyer un e-mail pour confirmer l'achat de produits.
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);
        });
    }
Si nous utilisons ThreadLocal pour enregistrer les informations utilisateur, une erreur cachée apparaîtra. Étant donné qu'un pool de threads est utilisé et que les threads peuvent être réutilisés, lors de l'utilisation de ThreadLocal pour obtenir des informations utilisateur, il peut afficher par erreur les informations de quelqu'un d'autre. Pour résoudre ce problème, vous devez utiliser des sessions.

Utilisez HashSet pour supprimer les données en double

Lors du codage, nous avons souvent besoin de déduplication. Lorsque l’on pense à la déduplication, la première chose à laquelle beaucoup de gens pensent est l’utilisation d’un HashSet . Cependant, une utilisation imprudente de HashSet peut entraîner l’échec de la déduplication.
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
Certains lecteurs attentifs devraient pouvoir deviner la raison de cet échec. HashSet utilise un code de hachage pour accéder à la table de hachage et utilise la méthode equals pour déterminer si les objets sont égaux. Si l'objet défini par l'utilisateur ne remplace pas la méthode hashcode et la méthode égale , alors la méthode hashcode et la méthode égale de l'objet parent seront utilisées par défaut. Cela amènera le HashSet à supposer qu’il s’agit de deux objets différents, provoquant l’échec de la déduplication.

Élimination d'un thread de pool "mangé"

ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(()->{
            //do something
            double result = 10/0;
        });
Le code ci-dessus simule un scénario dans lequel une exception est levée dans le pool de threads. Le code métier doit assumer diverses situations, il est donc très probable qu'il lèvera une RuntimeException pour une raison quelconque . Mais s'il n'y a pas de traitement spécial ici, alors cette exception sera « mangée » par le pool de threads. Et vous n’aurez même aucun moyen de vérifier la cause de l’exception. Par conséquent, il est préférable d’intercepter les exceptions dans le pool de processus.

Chaînes en Java - vue intérieure

Source : Medium L'auteur de cet article a décidé d'examiner en détail la création, les fonctionnalités et les caractéristiques des chaînes en Java. Pause café #146.  5 erreurs que commettent 99% des développeurs Java.  Chaînes en Java - vue intérieure - 2

Création

Une chaîne en Java peut être créée de deux manières différentes : implicitement, en tant que chaîne littérale, et explicitement, à l'aide du mot-clé new . Les chaînes littérales sont des caractères placés entre guillemets doubles.
String literal   = "Michael Jordan";
String object    = new String("Michael Jordan");
Bien que les deux déclarations créent un objet chaîne, il existe une différence dans la manière dont ces deux objets sont situés dans la mémoire tas.

Représentation interne

Auparavant, les chaînes étaient stockées sous la forme char[] , ce qui signifie que chaque caractère était un élément distinct dans le tableau de caractères. Comme ils étaient représentés au format de codage de caractères UTF-16 , cela signifiait que chaque caractère occupait deux octets de mémoire. Ce n'est pas très correct, car les statistiques d'utilisation montrent que la plupart des objets chaîne sont constitués uniquement de caractères Latin-1 . Les caractères Latin-1 peuvent être représentés à l'aide d'un seul octet de mémoire, ce qui peut réduire considérablement l'utilisation de la mémoire, jusqu'à 50 %. Une nouvelle fonctionnalité de chaîne interne a été implémentée dans le cadre de la version JDK 9 basée sur JEP 254 appelée Compact Strings. Dans cette version, char[] a été remplacé par byte[] et un champ d'indicateur d'encodeur a été ajouté pour représenter l'encodage utilisé (Latin-1 ou UTF-16). Après cela, le codage est effectué en fonction du contenu de la chaîne. Si la valeur contient uniquement des caractères Latin-1, alors le codage Latin-1 est utilisé (la classe StringLatin1 ) ou le codage UTF-16 est utilisé (la classe StringUTF16 ).

Allocation de mémoire

Comme indiqué précédemment, il existe une différence dans la manière dont la mémoire est allouée à ces objets sur le tas. L'utilisation du nouveau mot-clé explicite est assez simple puisque la JVM crée et alloue de la mémoire pour la variable sur le tas. Par conséquent, l’utilisation d’une chaîne littérale suit un processus appelé internement. L'internement de chaînes est le processus consistant à mettre des chaînes dans un pool. Il utilise une méthode permettant de stocker une seule copie de chaque valeur de chaîne individuelle, qui doit être immuable. Les valeurs individuelles sont stockées dans le pool String Intern. Ce pool est un magasin Hashtable qui stocke une référence à chaque objet chaîne créé à l'aide de littéraux et de son hachage. Bien que la valeur de chaîne se trouve sur le tas, sa référence peut être trouvée dans le pool interne. Cela peut être facilement vérifié à l’aide de l’expérience ci-dessous. Ici, nous avons deux variables avec la même valeur :
String firstName1   = "Michael";
String firstName2   = "Michael";
System.out.println(firstName1 == firstName2);             //true
Lors de l'exécution du code, lorsque la JVM rencontre firstName1 , elle recherche la valeur de chaîne dans le pool de chaînes interne Michael . S'il ne le trouve pas, une nouvelle entrée est créée pour l'objet dans le pool interne. Lorsque l'exécution atteint firstName2 , le processus se répète à nouveau et cette fois, la valeur peut être trouvée dans le pool en fonction de la variable firstName1 . De cette façon, au lieu de dupliquer et de créer une nouvelle entrée, le même lien est renvoyé. La condition d’égalité est donc satisfaite. En revanche, si une variable avec la valeur Michael est créée à l'aide du mot-clé new, aucun internement ne se produit et la condition d'égalité n'est pas satisfaite.
String firstName3 = new String("Michael");
System.out.println(firstName3 == firstName2);           //false
Le stage peut être utilisé avec la méthode firstName3 intern() , bien que cela ne soit généralement pas préféré.
firstName3 = firstName3.intern();                      //Interning
System.out.println(firstName3 == firstName2);          //true
L'internement peut également se produire lors de la concaténation de deux chaînes littérales à l'aide de l' opérateur + .
String fullName = "Michael Jordan";
System.out.println(fullName == "Michael " + "Jordan");     //true
Nous voyons ici qu'au moment de la compilation, le compilateur ajoute les deux littéraux et supprime l' opérateur + de l'expression pour former une seule chaîne, comme indiqué ci-dessous. Au moment de l'exécution, fullName et le « littéral ajouté » sont internés et la condition d'égalité est satisfaite.
//After Compilation
System.out.println(fullName == "Michael Jordan");

Égalité

D'après les expériences ci-dessus, vous pouvez voir que seuls les littéraux de chaîne sont internés par défaut. Cependant, une application Java n'aura certainement pas que des chaînes littérales, puisqu'elle peut recevoir des chaînes provenant de différentes sources. Par conséquent, l’utilisation de l’opérateur d’égalité n’est pas recommandée et peut produire des résultats indésirables. Les tests d'égalité ne doivent être effectués que par la méthode des égalités . Il effectue l'égalité en fonction de la valeur de la chaîne plutôt que de l'adresse mémoire où elle est stockée.
System.out.println(firstName1.equals(firstName2));       //true
System.out.println(firstName3.equals(firstName2));       //true
Il existe également une version légèrement modifiée de la méthode equals appelée equalsIgnoreCase . Cela peut être utile à des fins insensibles à la casse.
String firstName4 = "miCHAEL";
System.out.println(firstName4.equalsIgnoreCase(firstName1));  //true

Immutabilité

Les chaînes sont immuables, ce qui signifie que leur état interne ne peut pas être modifié une fois créées. Vous pouvez modifier la valeur d'une variable, mais pas la valeur de la chaîne elle-même. Chaque méthode de la classe String qui traite de la manipulation d'un objet (par exemple, concat , substring ) renvoie une nouvelle copie de la valeur plutôt que de mettre à jour la valeur existante.
String firstName  = "Michael";
String lastName   = "Jordan";
firstName.concat(lastName);

System.out.println(firstName);                       //Michael
System.out.println(lastName);                        //Jordan
Comme vous pouvez le constater, aucune modification n'est apportée aux variables : ni firstName ni lastName . Les méthodes de classe String ne changent pas l’état interne, elles créent une nouvelle copie du résultat et renvoient le résultat comme indiqué ci-dessous.
firstName = firstName.concat(lastName);

System.out.println(firstName);                      //MichaelJordan
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION