JavaRush /Blog Java /Random-FR /Erreurs des programmeurs Java débutants. Partie 2
articles
Niveau 15

Erreurs des programmeurs Java débutants. Partie 2

Publié dans le groupe Random-FR
Erreurs des programmeurs Java débutants. Partie 1

9. Appel de méthodes de classe non statiques à partir de la méthode main()

Le point d'entrée de tout programme Java doit être une méthode statiquemain :
Erreurs des programmeurs Java débutants.  Partie 2 - 1
public static void main(String[] args) {
  ...
}
Étant donné que cette méthode est statique, vous ne pouvez pas en appeler des méthodes de classe non statiques. Les étudiants oublient souvent cela et essaient d'appeler des méthodes sans créer d'instance de classe. Cette erreur est généralement commise au tout début de la formation, lorsque les étudiants rédigent de petits programmes. Mauvais exemple :
public class DivTest {
    boolean divisible(int x, int y) {
        return (x % y == 0);
    }

    public static void main(String[] args) {
        int v1 = 14;
        int v2 = 9;

        // на следующие строки компилятор выдаст ошибку
        if (divisible(v1, v2)) {
            System.out.println(v1 + " is a multiple of " + v2);
        } else {
            System.out.println(v2 + " does not divide " + v1);
        }
    }
}
Il existe 2 façons de corriger l'erreur : rendre la méthode souhaitée statique ou créer une instance de la classe. Pour choisir la bonne méthode, demandez-vous si la méthode utilise un champ ou d'autres méthodes de classe. Si oui, vous devez créer une instance de la classe et appeler une méthode dessus, sinon vous devez rendre la méthode statique. Exemple corrigé 1 :
public class DivTest {
    int modulus;

    public DivTest(int m) {
      modulus = m;
    }

    boolean divisible(int x) {
        return (x % modulus == 0);
    }

    public static void main(String[] args) {
        int v1 = 14;
        int v2 = 9;

        DivTest tester = new DivTest(v2);

        if (tester.divisible(v1) {
            System.out.println(v1 + " is a multiple of " + v2);
        } else {
            System.out.println(v2 + " does not divide " + v1);
        }
    }
}
Exemple corrigé 2 :
public class DivTest {
    static boolean divisible(int x, int y) {
        return (x % y == 0);
    }

    public static void main(String[] args) {
        int v1 = 14;
        int v2 = 9;

        if (divisible(v1, v2)) {
            System.out.println(v1 + " is a multiple of " + v2);
        } else {
            System.out.println(v2 + " does not divide " + v1);
        }
    }
}

10. Utilisation d'objets de classe String comme paramètres de méthode.

En Java, une classe java.lang.Stringstocke les données de chaîne. Cependant, les chaînes en Java
  1. avoir une permanence (c'est-à-dire qu'ils ne peuvent pas être modifiés),
  2. sont des objets.
Par conséquent, ils ne peuvent pas être traités comme un simple tampon de caractères ; ce sont des objets immuables. Parfois, les étudiants transmettent des chaînes en espérant à tort que l'objet chaîne sera transmis sous forme de tableau de caractères par référence (comme en C ou C++). Le compilateur ne considère généralement pas cela comme une erreur. Mauvais exemple.
public static void main(String args[]) {
   String test1 = "Today is ";
   appendTodaysDate(test1);
   System.out.println(test1);
}

/* прим. редактора: закомментированный метод должен иметь модификатор
    static (здесь автором допущена ошибка №9)
public void appendTodaysDate(String line) {
    line = line + (new Date()).toString();
}
*/

public static void appendTodaysDate(String line) {
    line = line + (new Date()).toString();
}
Dans l'exemple ci-dessus, l'étudiant souhaite modifier la valeur d'une variable locale test1en attribuant une nouvelle valeur à un paramètre linedans une méthode appendTodaysDate. Naturellement, cela ne fonctionnera pas. Le sens linechangera, mais le sens test1restera le même. Cette erreur se produit en raison d'un malentendu selon lequel (1) les objets Java sont toujours transmis par référence et (2) les chaînes en Java sont immuables. Vous devez comprendre que les objets chaîne ne changent jamais de valeur et que toutes les opérations sur les chaînes créent un nouvel objet. Pour corriger l'erreur dans l'exemple ci-dessus, vous devez soit renvoyer une chaîne de la méthode, soit transmettre un objet StringBufferen tant que paramètre à la méthode au lieu de String. Exemple corrigé 1 :
public static void main(String args[]) {
   String test1 = "Today is ";
   test1 = appendTodaysDate(test1);
   System.out.println(test1);
}

public static String appendTodaysDate(String line) {
    return (line + (new Date()).toString());
}
Exemple corrigé 2 :
public static void main(String args[]) {
   StringBuffer test1 = new StringBuffer("Today is ");
   appendTodaysDate(test1);
   System.out.println(test1.toString());
}

public static void appendTodaysDate(StringBuffer line) {
    line.append((new Date()).toString());
}

environ. traduction
En fait, il n’est pas si facile de comprendre quelle est l’erreur. Puisque les objets sont passés par référence, cela signifie linequ'ils font référence au même endroit que test1. Cela signifie qu'en en créant un nouveau line, nous en créons un nouveau test1. Dans le mauvais exemple, tout semble être un transfert Stringpar valeur et non par référence.

11. Déclarer un constructeur comme méthode

Les constructeurs d'objets en Java ressemblent en apparence aux méthodes classiques. Les seules différences sont que le constructeur ne spécifie pas de type de retour et que le nom est le même que le nom de la classe. Malheureusement, Java permet que le nom de la méthode soit le même que le nom de la classe. Dans l'exemple ci-dessous, l'étudiant souhaite initialiser un champ classe Vector listlors de la création de la classe. Cela n'arrivera pas car une méthode 'IntList'n'est pas un constructeur. Mauvais exemple.
public class IntList {
    Vector list;

    // Выглядит How конструктор, но на самом деле это метод
    public void IntList() {
        list = new Vector();
    }

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
Le code lèvera une exception NullPointerExceptionlors du premier accès au champ list. L'erreur est facile à corriger : il vous suffit de supprimer la valeur de retour de l'en-tête de la méthode. Exemple corrigé :
public class IntList {
    Vector list;

    // Это конструктор
    public IntList() {
        list = new Vector();
    }

    public append(int n) {
        list.addElement(new Integer(n));
    }
}

12. J'ai oublié de convertir un objet au type requis

Comme tous les autres langages orientés objet, en Java, vous pouvez désigner un objet comme sa superclasse. C'est ce qu'on appelle 'upcasting', cela se fait automatiquement en Java. Cependant, si une valeur de retour de variable, de champ de classe ou de méthode est déclarée comme superclasse, les champs et méthodes de la sous-classe seront invisibles. En faisant référence à une superclasse en tant que sous-classe 'downcasting', vous devez l'enregistrer vous-même (c'est-à-dire amener l'objet dans la sous-classe souhaitée). Les étudiants oublient souvent de sous-classer un objet. Cela se produit le plus souvent lors de l'utilisation de tableaux d'objets et de collections à partir d'un package java.util(c'est-à-dire le Collection Framework ). L'exemple ci-dessous Stringplace un objet dans un tableau, puis le supprime du tableau pour le comparer à une autre chaîne. Le compilateur détectera une erreur et ne compilera pas le code tant qu'un transtypage n'est pas explicitement spécifié. Mauvais exemple.
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');

String arg = args[0];
if (arr[0].compareTo(arg) < 0) {
    System.out.println(arg + " comes before " + arr[0]);
}
La signification du casting de caractères est difficile pour certains. Les méthodes dynamiques causent souvent des difficultés. Dans l'exemple ci-dessus, si la méthode avait été utilisée equalsà la place de compareTo, le compilateur n'aurait pas renvoyé d'erreur et le code aurait fonctionné correctement, puisque la méthode equalsde la classe aurait été appelée String. Vous devez comprendre que les liens dynamiques sont différents des downcasting. Exemple corrigé :
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');

String arg = args[0];
if ( ((String) arr[0]).compareTo(arg) < 0) {
    System.out.println(arg + " comes before " + arr[0]);
}

13. Utiliser les interfaces.

Pour de nombreux étudiants, la différence entre les classes et les interfaces n’est pas tout à fait claire. Par conséquent, certains étudiants essaient d'implémenter des interfaces telles que Observerou Runnableen utilisant le mot-clé extends au lieu de Implements . Pour corriger l'erreur, il vous suffit de corriger le mot-clé par le bon. Mauvais exemple :
public class SharkSim extends Runnable {
    float length;
    ...
}
Exemple corrigé :
public class SharkSim implements Runnable {
    float length;
    ...
}
Erreur associée : ordre incorrect des blocs d'extension et d'implémentation . Selon la spécification Java, les déclarations d'extension de classe doivent précéder les déclarations d'implémentation d'interface. De plus, pour les interfaces, le mot-clé Implements ne doit être écrit qu'une seule fois ; plusieurs interfaces sont séparées par des virgules. Quelques exemples plus erronés :
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
    int airLeft;
    ...
}
Exemples corrigés :
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
    float length;
    ...
}

// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
    int airLeft;
    ...
}

14. J'ai oublié d'utiliser la valeur de retour d'une méthode de superclasse

Java vous permet d'appeler une méthode de superclasse similaire à partir d'une sous-classe à l'aide du mot-clé mot-clé. Parfois, les étudiants doivent appeler des méthodes de superclasse, mais oublient souvent d'utiliser la valeur de retour. Cela arrive particulièrement souvent chez les étudiants qui n'ont pas encore compris les méthodes et leurs valeurs de retour. Dans l'exemple ci-dessous, un étudiant souhaite insérer le résultat d'une toString()méthode de superclasse dans le résultat d'une toString()méthode de sous-classe. Cependant, il n’utilise pas la valeur de retour de la méthode superclasse. Mauvais exemple :
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Pour corriger l'erreur, il suffit généralement d'attribuer la valeur de retour à une variable locale, puis d'utiliser cette variable lors du calcul du résultat de la méthode de sous-classe. Exemple corrigé :
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. J'ai oublié d'ajouter des composants AWT

AWT utilise un modèle de conception d'interface graphique simple : chaque composant d'interface doit d'abord être créé à l'aide de son propre constructeur, puis placé dans la fenêtre de l'application à l'aide d'une add()méthode de composant parent. Ainsi, l'interface sur AWT reçoit une structure hiérarchique. Les étudiants oublient parfois ces 2 étapes. Ils créent un composant mais oublient de le placer dans la fenêtre d'agrandissement. Cela ne provoquera pas d'erreurs au stade de la compilation, le composant n'apparaîtra tout simplement pas dans la fenêtre de l'application. Mauvais exemple.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Pour corriger cette erreur, il vous suffit d'ajouter les composants à leurs parents. L'exemple ci-dessous montre comment procéder. Il convient de noter que souvent un étudiant qui oublie d'ajouter un composant à une fenêtre d'application oublie également d'attribuer des écouteurs d'événements à ce composant. Exemple corrigé :
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");

        exit = new Button("Quit");

        Panel controlPanel = new Panel();
        controlPanel.add(exit);

        add("Center", controlPanel);

        exit.addActionListener(this);
    }

    public void actionPerformed(ActionEvent e) {
        System.exit(0);
    }
}

17. J'ai oublié de démarrer le flux

Le multithreading en Java est implémenté à l'aide du java.lang.Thread. Le cycle de vie d'un thread se compose de 4 étapes : initialisé, démarré, bloqué et arrêté. Le thread nouvellement créé est dans un état initialisé. Pour le mettre en état d'exécution, vous devez l'appeler start(). Parfois, les étudiants créent des fils de discussion mais oublient de les démarrer. Habituellement, l'erreur se produit lorsque l'étudiant n'a pas suffisamment de connaissances en programmation parallèle et en multithreading. (trad. approx. : je ne vois pas le lien) Pour corriger l'erreur, il vous suffit de démarrer le fil de discussion. Dans l'exemple ci-dessous, un étudiant souhaite créer une animation d'une image à l'aide de l'interface Runnable, mais il a oublié de démarrer le fil de discussion. Mauvais exemple
public class AnimCanvas extends Canvas implements Runnable {
        protected Thread myThread;
        public AnimCanvas() {
                myThread = new Thread(this);
        }

        // метод run() не будет вызван,
        // потому что поток не запущен.
        public void run() {
                for(int n = 0; n < 10000; n++) {
                   try {
                     Thread.sleep(100);
                   } catch (InterruptedException e) { }

                   animateStep(n);
                }
        }
        ...
}
Exemple corrigé :
public class AnimCanvas extends Canvas implements Runnable {
        static final int LIMIT = 10000;
        protected Thread myThread;

        public AnimCanvas() {
                myThread = new Thread(this);
                myThread.start();
        }

        public void run() {
                for(int n = 0; n < LIMIT; n++) {
                        try {
                          Thread.sleep(100);
                        } catch (InterruptedException e) { }

                        animateStep(n);
                }
        }
        ...
}
Le cycle de vie d'un thread et la relation entre les threads et les classes qui implémentent une interface Runnableconstituent une partie très importante de la programmation Java, et il serait utile de se concentrer sur cela.

18. Utilisation de la méthode interdite readLine() de la classe java.io.DataInputStream

readLine()Dans Java version 1.0, vous deviez utiliser une méthode de classe pour lire une chaîne de texte java.io.DataInputStream. Java version 1.1 a ajouté tout un ensemble de classes d'E/S pour fournir des opérations d'E/S pour le texte : les Readeret Writer. Ainsi, à partir de la version 1.1 pour lire une ligne de texte, vous devez utiliser la méthode readLine()class java.io.BufferedReader. Les étudiants peuvent ne pas être conscients de ce changement, surtout s’ils ont appris à partir de livres plus anciens. (Traduction approximative : en fait, ce n'est plus d'actualité. Il est peu probable que quiconque étudie désormais à partir de livres vieux de 10 ans). L'ancienne méthode readLine()reste dans le JDK, mais est déclarée illégale, ce qui déroute souvent les étudiants. Ce que vous devez comprendre, c’est que l’utilisation d’une méthode readLine()de classe java.io.DataInputStreamn’est pas une erreur, elle est simplement obsolète. Vous devez utiliser la classe BufferedReader. Mauvais exemple :
public class LineReader {
    private DataInputStream dis;

    public LineReader(InputStream is) {
        dis = new DataInputStream(is);
    }

    public String getLine() {
        String ret = null;

        try {
          ret = dis.readLine();  // Неправильно! Запрещено.
        } catch (IOException ie) { }

        return ret;
    }
}
Exemple corrigé :
public class LineReader {
    private BufferedReader br;

    public LineReader(InputStream is) {
        br = new BufferedReader(new InputStreamReader(is));
    }

    public String getLine() {
        String ret = null;

        try {
          ret = br.readLine();
        } catch (IOException ie) { }

        return ret;
    }
}
Il existe d'autres méthodes interdites dans les versions ultérieures à 1.0, mais celle-ci est la plus courante.

19. Utiliser double comme flotteur

Comme la plupart des autres langages, Java prend en charge les opérations sur les nombres à virgule flottante (nombres fractionnaires). Java propose deux types primitifs pour les nombres à virgule flottante : doubleune précision IEEE de 64 bits et floatune précision IEEE de 32 bits. La difficulté réside dans l'utilisation de nombres décimaux tels que 1,75, 12.9e17 ou -0.00003 - le compilateur les attribue au type double. Java n'effectue pas de transtypage dans les opérations où une perte de précision peut survenir. Ce casting de type doit être effectué par le programmeur. Par exemple, Java ne vous permettra pas d'attribuer une valeur de type à une intvariable de type bytesans conversion de type, comme le montre l'exemple ci-dessous.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Étant donné que les nombres fractionnaires sont représentés par type doubleet que l'affectation doubleà une variable de type floatpeut entraîner une perte de précision, le compilateur se plaindra de toute tentative d'utilisation de nombres fractionnaires comme float. Ainsi, l’utilisation des devoirs ci-dessous empêchera la classe de se compiler.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Cette mission fonctionnerait en C ou C++, mais en Java, elle est beaucoup plus stricte. Il existe 3 façons de se débarrasser de cette erreur. Vous pouvez utiliser le type doubleau lieu de float. C'est la solution la plus simple. En fait, il ne sert à rien d'utiliser l'arithmétique 32 bits au lieu de 64 bits ; la différence de vitesse est toujours absorbée par la JVM (d'ailleurs, dans les processeurs modernes, tous les nombres fractionnaires sont convertis au format d'un processeur 80 bits s'inscrire avant toute opération). Le seul avantage de leur utilisation floatest qu'ils occupent moins de mémoire, ce qui est utile lorsque l'on travaille avec un grand nombre de variables fractionnaires. Vous pouvez utiliser un modificateur de type numérique pour indiquer au compilateur comment stocker le numéro. Modificateur de type float - 'f'. Ainsi, le compilateur attribuera le type 1.75 à double, et 1.75f - float. Par exemple:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Vous pouvez utiliser le casting de type explicite. C'est la manière la moins élégante, mais elle est utile lors de la conversion d'une variable de type doubleen type float. Exemple:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Vous pouvez en savoir plus sur les nombres à virgule flottante ici et ici.

-- commentaire du traducteur --
C'est tout.
Dans l'exemple 10, l'erreur 9 a été commise. Je l'ai remarqué tout de suite, mais j'ai oublié d'écrire une note. mais ne l'a pas corrigé afin qu'il n'y ait pas de divergences avec la source originale.

Auteur : A.Grasoff™ Lien vers la source : Erreurs des programmeurs Java débutants
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION