Pièges de Java. Partie 1

Bonjour Je voudrais attirer votre attention sur un court article. Cet article est destiné aux débutants. Mais même si vous êtes un développeur expérimenté, ne tirez pas de conclusions hâtives.
J'espère que cette publication sera utile non seulement aux débutants.

Le but de cette publication:
Montrez les erreurs les plus courantes aux débutants et quelques astuces pour les corriger. Il est clair que certaines erreurs peuvent être complexes et se produire pour une raison ou une autre. Le but de la publication est dans une certaine mesure de les analyser et d'aider à les identifier à un stade précoce. J'espère que cette publication sera utile aux débutants.

Liste de vérification des erreurs:

1. Typos. Des fautes de frappe ennuyeuses qui n'apparaissent pas immédiatement
2. Affectation en condition au lieu de comparaison
3. Erreurs logiques dans la condition
4. Mauvaise comparaison de chaînes
5. Initialisation incorrecte des variables de types primitifs
6. Mauvaise utilisation du double
7. Type de valeur de retour incorrect dans le constructeur.
8. Division par zéro. POSITIVE_INFINITY
9. Ne pas prendre en compte l'ordre d'initialisation des classes
10. Une variable locale cache une variable de classe
11. Ignorer la conversion automatique dans les expressions arithmétiques
12. Boucle infinie avec octet, difficile à détecter.
13. Le nom de la classe est différent du nom du fichier dans lequel elle est stockée.
14. Les objets qui sont des éléments d'un tableau ne sont pas initialisés.
15. Placer plusieurs classes dans un fichier à la fois avec le modificateur public

Pièges Java


Tous les langages de programmation ont leurs avantages et leurs inconvénients. Cela est dû à de nombreuses raisons. Java ne fait pas exception. J'ai essayé de recueillir des difficultés évidentes et non évidentes rencontrées par un programmeur Java novice. Je suis sûr que les programmeurs expérimentés trouveront également quelque chose d'utile dans mon article. La pratique, l'attention et l'expérience acquise en programmation vous aideront à éviter de nombreuses erreurs. Mais il est préférable de considérer à l'avance certaines erreurs et difficultés. Je vais donner quelques exemples avec du code et des explications. De nombreuses explications vous apparaîtront clairement à partir des commentaires sur le code. La pratique donne beaucoup, car certaines règles ne sont pas si évidentes. Certains se trouvent à la surface, certains sont cachés dans les bibliothèques de langues ou dans la machine virtuelle java. N'oubliez pas que java n'est pas seulement un langage de programmation avec un ensemble de bibliothèques, c'est aussi une machine virtuelle java.

Pour l'article, j'ai spécifiquement écrit un code de travail avec des commentaires détaillés. Pour écrire un article avec des exemples de code, java 8 a été utilisé. Pour les tests, le code java est placé dans des packages séparés.

Exemple: "package underwaterRocks.simple;"

À quelles difficultés les débutants sont-ils confrontés?

Typos


Il arrive que des programmeurs novices créent des fautes de frappe difficiles à détecter en un coup d'œil.


Exemple de code:

Fichier: "Simple.java"

/*   ;     */ package underwaterRocks.simple; /** * * @author Ar20L80 */ public class Simple { public static void main(String[] args) { int ival = 10; if(ival>0); { System.out.println("     "); } } } 


Explication : «Le point-virgule indique la fin de l'instruction. Dans ce cas; Est la fin d'une instruction vide. C'est une erreur logique. Une telle erreur peut être difficile à détecter.

Le compilateur considérera que tout est correct. Condition si (ival> 0); dans ce cas n'a pas de sens. Parce que cela signifie: si ival est supérieur à zéro, ne faites rien et continuez. »

Affectation en condition au lieu de comparaison


La condition est l'affectation variable.

Ce n'est pas une erreur, mais l'utilisation d'une telle technique doit être justifiée.

  boolean myBool = false; if(myBool = true) System.out.println(myBool); 

Dans ce code, si (myBool = true) signifie: "Définissez la variable myBool sur true,
si l'expression est vraie, suivez la condition suivant les crochets. »

Dans ce code, la condition sera toujours vraie. Et System.out.println (myBool); sera toujours exécuté, quelle que soit la condition.

== est une comparaison pour l'égalité.
= Est une affectation, vous pouvez dire a = 10; comme: "mais attribuez une valeur de 10".

La condition entre parenthèses renvoie une valeur booléenne.
Peu importe l'ordre dans lequel vous écrivez. Vous pouvez comparer comme ceci: (0 == a) ou (5 == a)
Si vous oubliez un signe égal, par exemple (0 = a) ou (5 = a), le compilateur vous informera d'une erreur. Vous attribuez une valeur, pas une comparaison.
Vous pouvez également écrire sous une forme lisible un certain intervalle.
Par exemple: vous devez écrire: supérieur à 5 et inférieur à 10.
Vous écrivez comme ceci: (a> 4 && a <10), mais avec le même succès, vous pouvez écrire: (4 <a && a <10),
vous voyez maintenant que a est compris entre 4 et 10, à l'exclusion de ces valeurs. C'est plus évident. Il est immédiatement évident que, a est dans l'intervalle entre 4 et 10, à l'exclusion de ces valeurs.

Exemple de code (intervalle) 3,9 [):
si (3 <a && a <9) exécuter;

Erreur logique


if (condition) {} if (condition) {} else {} - else fait référence à l'if le plus proche.
C'est souvent la cause d'erreurs débutantes.

Comparaison de chaînes non valide

Les débutants utilisent assez souvent == au lieu de .equals pour comparer les chaînes.

Initialisation variable


Envisagez d'initialiser des variables d'un type primitif.

Primitives (octet, court, int, long, char, float, double, booléen).

Valeurs initiales.

 byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char '\u0000' String (or any object) null boolean false (  jvm) 


Remarque:

Les variables locales sont légèrement différentes;
Le compilateur n'attribue jamais de valeur par défaut à une variable locale non initialisée.

Si vous ne pouvez pas initialiser votre variable locale là où elle est déclarée,
N'oubliez pas de lui attribuer une valeur avant d'essayer de l'utiliser.

L'accès à une variable locale non initialisée entraînera une erreur de compilation.

Confirmation de cette note dans le code:

Fichier: "MyInitLocal.java"

 /*         */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocal { float classes_f; int classes_gi; public static void main(String[] args) { float f; int i; MyInitLocal myInit = new MyInitLocal(); /*         .*/ System.out.println("myInit.classes_f = " + myInit.classes_f); System.out.println("myInit.classes_gi = " + myInit.classes_gi); // System.out.println("f = " + f); // .     // System.out.println("f = " + i); // .     } } 


Plages de valeurs:

byte ( , 1 , [-128, 127])
short ( , 2 , [-32768, 32767])
int ( , 4 , [-2147483648, 2147483647])
long ( , 8 , [-922372036854775808,922372036854775807])
float ( , 4 )
double ( , 8 )
char ( Unicode, 2 , 16 , [0, 65535])
boolean ( /, int, JVM)

char: le type de données char est un seul caractère Unicode 16 bits. Il a une valeur minimale de «\ u0000» (ou 0) et une valeur maximale de «\ uffff» (ou 65 535 inclus).


Documentation Oracle >>

Essayons d'initialiser une variable de type long avec le numéro: 922372036854775807.
Rien ne marchera pour nous. Parce que c'est un littéral entier de type int.
Initialisation correcte avec un long littéral: 922372036854775807L;

Exemple de code:

Fichier: «MyInitLocalLong.java»

 /*    long  */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocalLong { public static void main(String[] args) { // long al = 922372036854775807; // integer number too large long bl = 922372036854775807L; //   } } 


Que rechercher lors de l'initialisation d'une variable.

Plage de valeurs d'une variable de ce type. Le fait que la variable soit initialisée avec un littéral d'un certain type. Pour les conversions explicites et implicites. Sur la compatibilité des types.

Lorsque vous utilisez des coques de type Integer, vous devez faire attention à l'emballage automatique et au déballage automatique de ces types.

Utilisation inappropriée du double


Ici, vous devez clarifier. Il ne s'agit pas de mal utiliser le double type.
Nous utilisons correctement. Seul le résultat peut surprendre un programmeur débutant.
Fichier: "MinusDouble.java"
 /*   */ package underwaterRocks.tstDouble; /** * * @author vvm */ public class MinusDouble { public static void main(String[] args) { double a = 4.64; double b = 2.64; System.out.println("ab = "+(ab)); } } /*   run: ab = 1.9999999999999996 */ 


Remarque sur le type double. Une virgule flottante vous permet de compter avec une erreur relative donnée et une plage énorme. Dans les calculs scientifiques, une erreur relative est souvent nécessaire.

Double comparaison non valide


Considérez le type double.

Exemple de code:

Fichier: "MyDouble.java"

 /*    double  - double. */ package underwaterRocks.myDouble; /** * * @author Ar20L80 */ public class MyDouble { public static void main(String[] args) { double dx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1; System.out.println("dx = " + dx); // dx = 0.9999999999999997 System.out.print(" (dx == 1.0):"); System.out.println(dx == 1.0); // false,   1.0   0.9999999999999997 /*   double*/ final double EPSILON = 1E-14; double xx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1; double xy = 1.0; /*  xx c xy */ if (Math.abs(xx - xy) < EPSILON) System.out.println(xx + "    " + xy + " EPSILON = " + EPSILON); } } 

Le type double est pratique lorsqu'une précision élevée n'est pas nécessaire. Pour les transactions financières, ce type ne convient pas. Bien que certaines entreprises, pas très honnêtes, utilisent le type double pour arrondir le côté dont elles ont besoin. Pour les opérations financières, la classe BigDecimal est utilisée dans les calculs financiers, car les vrais types primitifs ne conviennent pas à cette fin pour des raisons de perte de précision et d'erreurs dans les résultats d'arrondi. Cependant, des résultats plus précis sont obtenus en utilisant la classe BigInteger.

Constructeur de classe


Le constructeur de classe correspond au nom de classe et ne renvoie rien, pas même void.

Exemple de code:

Fichier: "MyConstructor.java"

 /*      ,  void    void -    */ package underwaterRocks.myConstructor; /** * * @author Ar20L80 */ public class MyConstructor { public MyConstructor(){ System.out.println("   void"); } public void MyConstructor(){ System.out.println("  c void"); } public static void main(String[] args) { MyConstructor myconst = new MyConstructor(); myconst.MyConstructor(); //    } } 

Comme nous le voyons dans le code, deux méthodes avec les mêmes noms: MyConstructor () et MyConstructor (). L'une des méthodes ne renvoie rien. Ceci est le constructeur de notre classe. Une autre méthode avec void est la méthode de classe régulière. Dans le cas où vous n'avez pas créé de constructeur ou, à votre avis, créé un constructeur de classe avec void, le compilateur créera un constructeur par défaut et vous serez surpris de savoir pourquoi votre constructeur ne fonctionne pas.

Division par zéro


Que pensez-vous sera le résultat de l'exécution de ce code.

Fichier: "DivisionByZero.java"

 /* */ package divisionByZero; import static java.lang.Double.POSITIVE_INFINITY; /** * * @author Ar20L80 */ public class DivisionByZero { public static void main(String[] args) { try{ float f = 12.2f; double d = 8098098.8790d; System.out.println(f/0); System.out.println(d/0); System.out.println(POSITIVE_INFINITY == f/0); System.out.println(POSITIVE_INFINITY == d/0); } catch (NumberFormatException ex) { System.out.println("NumberFormatException"); } catch (ArithmeticException ex) { System.out.println("ArithmeticException"); } } } 

L'exécution du code produira:

 Infinity Infinity true true 

La division du type entier par zéro donnera une ArithmeticException.

La classe java.lang.Double définit la constante POSITIVE_INFINITY;

 public static final float POSITIVE_INFINITY = 1.0d / 0.0d; 

Il est converti en une chaîne égale à Infinity.

Ordre d'initialisation


Fichier: «InitClass.java»

 /*     */ package myInitClass; /** * * @author Ar20L80 */ public class InitClass { InitClass(){ //   System.out.print(""); } { //   System.out.print("3 "); } public static void main(String[] args) { System.out.print("2"); new InitClass(); } static { //    System.out.print("1"); } } 

Tout d'abord, tous les blocs statiques sont exécutés, puis les blocs d'initialisation, puis le constructeur de classe.

Il sera affiché: "123 Constructor"

Une variable locale cache une variable de classe
Bien que les IDE modernes détectent facilement une telle erreur, je voudrais examiner une telle erreur plus en détail. Commençons par l'affectation classique des variables dans le constructeur. L'exemple est correct. Il n'y a pas d'erreur.
 public class MyClass { private int val = 0; public MyClass(int val) { this.val = val; } } 

Cependant, que se passe-t-il si vous utilisez cette technique dans une méthode et non dans un constructeur de classe? Dans la méthode habituelle, l'utilisation de cette technique n'est pas recommandée. La question porte sur la bonne conception d'une classe.

Une explication simple: dans une méthode, une variable portant le même nom qu'une variable de classe est locale à la méthode. Vous pouvez accéder à la variable de classe à l'aide de this.val. Cependant, un tel appel de la méthode, si la classe est mal conçue, ne provoquera que des effets secondaires et peut dégrader la lisibilité du code.

La conversion de type arithmétique se fait automatiquement

Cela peut provoquer des erreurs gênantes.
 // byte a = 1; // byte b = 1; // byte  = a + b; //  // byte a = (byte) 1; // byte b = (byte) 1; // byte  = a + b; //  


 //     —     . byte a = 1; byte b = 1; byte c = (byte) (a + b); 


 //     —  final // final byte a = 1; // final byte b = 1; // byte c = a + b; //    ,  a  b final 


Une solution possible lorsque vous travaillez avec une chaîne:
 byte bHundr = Byte.parseByte("100"); //      byte 


Une autre erreur est donnée dans le code suivant.
 for (byte i = 1; i <= 128; i++) { System.out.println(i); } 

Dans ce cas, nous obtenons une boucle infinie.

L'explication. Octet de type [-128, 127]. 128 n'est plus dans cette gamme. Un débordement se produit et le cycle se répète. La nécessité d'utiliser l'octet dans ce cas est douteuse. Bien qu'il ait lieu dans de rares cas. La recommandation est d'utiliser int au lieu d'octet. Une autre recommandation est de ne pas utiliser de boucle dans votre algorithme.

Les objets qui sont des éléments de tableau ne sont pas initialisés
 int[] cats = new int[10]; for(int i=0; i<cats.length;i++){ System.out.println("cats " + i + " = " + cats[i]); } 


Dans cet exemple, nous avons un tableau d'éléments de type primitif. Et rien de mal ne se produit lorsque nous ne les initialisons pas. On leur attribuera la valeur par défaut. Dans ce cas, la valeur = 0.

Prenons un autre exemple, non pas avec des primitives dans le tableau, mais avec des objets dans le tableau.
 public class ArrInitObj { public static void main(String[] args) { MyObj[] cats = new MyObj[10]; for(int i=0; i<cats.length;i++){ System.out.println("cats " + i + " = " + cats[i]); System.out.println("cats " + i + ".val = " + cats[i].val); //    java.lang.NullPointerException } } } class MyObj{ public int val; } 


La solution à ce problème consiste à initialiser toutes les variables d'objet avant de les utiliser. L'initialisation peut être effectuée dans le constructeur de la classe MyObj.

Le nom de classe est différent du nom du fichier dans lequel il est stocké
L'IDE moderne détecte facilement ce type d'erreur. Cependant, de telles erreurs sont rencontrées, bien que rarement. Cela aidera à l'attention, en tenant compte des différences dans les noms des lettres majuscules et minuscules.

Placer plusieurs classes dans un fichier à la fois avec le modificateur public
L'erreur est assez rare. L'IDE vous donnera immédiatement un avertissement.
Le nom de fichier doit correspondre au nom de la classe publique.

Conclusions
De nombreuses erreurs ne sont pas évidentes à première vue. Même les programmeurs expérimentés les font, mais en plus petit nombre. La prudence, l'expérience pratique, l'utilisation d'un débogueur et la lecture de la documentation vous aideront à éviter de nombreuses erreurs.

J'espère que vous avez apprécié l'article et que vous l'avez trouvé utile. Je serai heureux de vos commentaires, commentaires, suggestions, souhaits. À suivre. Au contraire, l'ajout suit.

Les références
Consignes de conception du code Oracle Java >>>

PS. Mes amis. Je n'ai aucun moyen de continuer à publier sans votre aide. Autrement dit, aucune opportunité financière. Si la publication vous a vraiment aidé et que vous souhaitez continuer, merci de me soutenir. Quelque part, il y a un bouton: "Soutenez l'auteur".
J'espère pour votre compréhension. Je vous remercie Merci à Habr pour l'opportunité de publier.

L'auteur sera obligé de supprimer ses publications ou de les masquer dans des brouillons en cas de manque de soutien. Ce n'est pas un ultimatum. Si vous en avez l'occasion, cela vous a été utile, vous pouvez aider, puis cliquez sur le bouton de support d'auteur.

Source: https://habr.com/ru/post/fr439642/


All Articles