Retour sur le langage Java

Introduction

Java est un langage de programmation orientée objet, créé par James Gosling et Patrick Naughton en 1990 pour la compagnie Sun Microsystème (qui a été vendu à Oracle en 2009).

Voici les caractéristiques principales du langage Java:

    • Langage orienté objet par classes;
    • Statiquement et fortement typé;
    • Gestion automatique de la mémoire (ramasse-miette ou « Garbage Collector »);
    • Langage compilé vers un code de machine virtuelle (appelé la JVM).
    • Possède une version libre (OpenJDK) et une version propriétaire (Oracle JDK).
    • Les versions java supportées sont: Java 7, 8, 11 et 17.
    • Les nouveaux utilisateurs devraient utiliser une des versions suivantes:
      • Java 11 (version LTS ou « Long Time Support »): Pour les utilisateurs qui veulent s’assurer l’utilisation long terme de leurs logiciels.
      • Java dernière version (présentement 17): Pour les utilisateurs qui veulent les dernières fonctionnalités de Java et qui n’ont pas de problème à effectuer des modifications au code lors d’une nouvelle version de Java.

La suite de développement Java (ou JDK)

La JDK contient une grande quantité d’outils. Mais nous allons voir les trois (3) outils principaux dans cette section. Il va de soi que cette section ne présente qu’une base et que ces outils peuvent nécessiter beaucoup plus d’options que ceux présentés.

Javac: Le compilateur Java

La commande javac permet la compilation d’une ou plusieurs classes Java. Voici un exemple d’utilisation de la commande javac.

javac MaClasse.java

Si les classes spécifiées dans la commande utilisent d’autres classes dont le fichier .java est accessible, les autres classes seront également compilées. Il n’est donc pas nécessaire de spécifier l’entièreté des classes du système dans la ligne de commande. En général, seule la classe contenant la fonction principale main doit être compilée.

Java: Lanceur d’application Java

La commande java permet d’exécuter un programme Java dans la JVM du JDK. Il est important de noter que l’argument de cette commande est le nom de la classe à exécuter (contenant la routine principale main), et non le nom du fichier. Voici un exemple d’utilisation de la commande:

java MaClasse

La classe spécifiée en argument doit avoir préalablement été compilée par la commande javac.

Si nous souhaitons envoyer des arguments aux programmes, on peut les spécifier après le nom de la classe. Par exemple:

java MaClasse Argument1 Argument2 Argument3

Les arguments sont accessibles en utilisant le tableau reçu en argument de la routine principale main.

jdb: Le débogueur Java

Pour effectuer du débogage, il est généralement mieux d’avoir une interface graphique. Par contre, il peut arriver qu’il soit important de pouvoir effectuer du débogage en ligne de commande. Par exemple, si l’ordinateur ayant le problème se trouve sur un ordinateur sans éditeur haut niveau (IDE). Ou bien lorsqu’on veut déboguer sur un serveur distant. La commande jdb permet ce débogage en ligne de commande.

Premièrement, pour utiliser le débogueur Java, vous devez compiler avec l’option de déboguage, c’est à dire « -g »:

javac -g MaClasse.java

Ensuite, pour lancer le débogueur, on lance la commande jdb de la même manière que nous lancerions la commande java. Par exemple:

jdb MaClasse

ou

jdb MaClasse Argument1 Argument2 Argument3

Ensuite, on doit mettre un point d’arrêt (ou « breakpoint ») en utilisant la commande stop. On peut arrêter à une ligne en particulier comme ceci:

stop at MaClasse:17

Ou bien on peut arrêter à une routine particulière comme ceci:

stop in MaClasse.main

Une fois le ou les points d’arrêt sont précisés, on peut lancer l’application avec la commande run:

run

Pour évaluer le contenu d’une variable, on peut utiliser la commande print:

print maVariable

Pour exécuter une ligne de code, on utilise la commande next:

next

Pour entrer dans un appel de routine, on utilise la commande step:

step

À noter que si la ligne de code n’est pas une routine, la commande step fait exactement la même chose que la commande next.
Pour continuer l’exécution normale du programme (après qu’un point d’arrêt ait été utilisé), on utilise la commande cont:

cont

La commande where permet de voir la pile d’exécution de la méthode en cours:

where

Finalement, la commande quit permet de terminer l’exécution du programme et de sortir du débogueur.

quit

La syntaxe de Java

Syntaxe d’une classe

En général, une classe se trouve dans son propre fichier (une classe par fichier) et le nom du fichier doit être identique au nom de la classe (sans prendre en compte l’extension « .java »). La syntaxe de base d’une classe Java se fait de la manière suivante:

public class MaClasse {

}

Prendre en note qu’à moins d’avoir une bonne raison de le faire, toutes les classes de fichiers java devraient être publiques.

La routine principale (« main »)

Pour qu’un programme Java puisse être exécuté, il doit y avoir une routine appelée main. Cette routine doit avoir la signature suivante:

public static void main(String[] arguments)

Utiliser une signature différente pour une routine principale en Java peut faire en sorte que le programme Java ne démarre pas. Il est donc important de bien respecter cette signature. De plus, une fois la routine principale créée, pour lancer le programme, il faut lancer la classe compilée avec le programme Java.

Il est à noter que lorsque nous commencerons à programmer en paradigme orienté objet, nous devrions avoir très peu de code à l’intérieur de cette routine. Généralement, on ne fait que lancer un constructeur d’un objet.

Les entrées/sorties console

Afin d’afficher des messages à l’utilisateur et d’obtenir des valeurs de l’utilisateur, il faut utiliser des mécanismes d’entrées/sorties. Il existe plusieurs mécanismes d’entrées/sorties en Java. Pour le début de ce cours, nous utiliserons l’entrée et la sortie console standard de Java.

Afficher un texte à l’utilisateur

Pour afficher un texte à l’utilisateur, nous utiliserons l’instruction suivante:

System.out.println("...");

Il est important de noter que cette instruction terminera la ligne inscrite à la console. Si vous voulez inscrire une seule ligne de sortie avec plusieurs instructions, on peut utiliser print au lieu de println:

System.out.print("...");

Afficher une erreur à l’utilisateur

Normalement, nous devrions utiliser la sortie d’erreur afin d’afficher une erreur à un utilisateur. Ça permet entre autres de voir les erreurs, même lorsque nous utilisons une redirection en ligne de commande. Pour utiliser la sortie d’erreur, on utiliser l’instruction suivante:

System.err.println("...");

Tout comme dans le cas de la sortie standard, on peut également utiliser print au lieu de println pour ne pas terminer la ligne affichée.

Voici un exemple:

public class Programme
{
    public static void main(String[] arguments) {
        System.out.println("Message standard.");
        System.err.println("Message d'erreur!");
    }
}

Si j’utilise ce programme normalement:

java Programme

J’obtiens le résultat attendu:

Message standard.
Message d'erreur!

Par contre, si je fais une redirection standard:

java Programme > mon_fichier.log

J’obtiens le résultat suivant dans la ligne de commande:

Message d'erreur!

Et le contenu du fichier « mon_fichier.log » devient:

Message standard.

Demander de l’information à l’utilisateur

Pour demander de l’information à l’utilisateur, il faut utiliser le « stream » System.in. Par contre, ce « stream » n’est pas utilisable directement (principalement à cause de l’absence d’héritage multiple en Java). Pour utiliser System.in, nous devons utiliser un objet utilitaire qui s’occupera de lire l’information. Dans le cadre du cours, je recommande d’utiliser un Scanner. Malgré certains petits défauts, il s’agit de la mécanique la plus simple d’utilisation. Par contre, si vous préférez une autre méthode, libre à vous de l’utiliser.

Pour utiliser un Scanner, il faut d’abord l’importer puisque ça ne fait pas partie d’une classe de base de Java:

import java.util.Scanner;

En suite, nous instancions l’objet Scanner en y spécifiant System.in comme entrée.

Scanner lScanner = new Scanner(System.in);

Ensuite, nous pouvons utiliser les utilitaires next[Type] pour lire un type quelconque au clavier. Par exemple, nous pouvons utiliser nextInt pour lire un entier, nextFloat pour lire un nombre à virgule, etc. Pour lire une ligne complète, nous utilisons nextLine.

Voici un exemple simple d’utilisation des entrées/sorties en Java:

import java.util.Scanner;

public class Programme
{
    public static void main(String[] arguments) {
        Scanner lScanner = new Scanner(System.in);
        String lNom;
        int lAge;
        System.out.print("Entrez votre nom: ");
        lNom = lScanner.nextLine();
        System.out.print("Entrez votre âge: ");
        lAge = lScanner.nextInt();
        System.out.println("Bonjour " + lNom + " de " + lAge + " ans.");
    }
}

Il est à noter qu’il est généralement une mauvaise idée d’alterner des nextInt (ou nextFloat) avec des nextLine. Puisque les nextInt (et nextFloat) ne lisent pas la fin de ligne, le résultat peut ne pas être ce qui est attendu. Par exemple, le code suivant ne fonctionnera pas:

import java.util.Scanner;

public class Programme
{
    public static void main(String[] arguments) {
        Scanner lScanner = new Scanner(System.in);
        String lNom;
        int lAge;
        System.out.print("Entrez votre âge: ");
        lAge = lScanner.nextInt();
        System.out.print("Entrez votre nom: ");
        lNom = lScanner.nextLine();
        System.out.println("Bonjour " + lNom + " de " + lAge + " ans.");
    }
}

Afin d’éviter d’avoir cette problématique, on peut utiliser seulement le nextLine et ensuite, transférer les valeurs lues en int ou en float avec des Integer.parseInt ou Float.parseFloat. Voici l’exemple précédent corrigé.

import java.util.Scanner;

public class Programme
{
    public static void main(String[] arguments) {
        Scanner lScanner = new Scanner(System.in);
        String lNom, lAgeTexte;
        int lAge;
        System.out.print("Entrez votre âge: ");
        lAgeTexte = lScanner.nextLine();
        lAge = Integer.parseInt(lAgeTexte);
        System.out.print("Entrez votre nom: ");
        lNom = lScanner.nextLine();
        System.out.println("Bonjour " + lNom + " de " + lAge + " ans.");
    }
}

Les variables en Java

Java est un langage typé. Il faut donc préciser le type de chaque variable dans sa déclaration. Il y a deux catégories de type en Java: les types primitifs et les types objets.

Les types primitifs

Certains types en Java ne représentent pas un objet (dans le sens qu’ils ne sont pas basés sur une classe de l’arbre d’héritage). On appelle ces types types primitifs. La caractéristique de ces types est qu’ils n’ont aucune méthode ni attribut et qu’ils ne peuvent pas être utilisés en contexte de polymorphisme (nous verrons ce concept plus tard). Voici un exemple de code utilisant les différents types primitifs accessibles en Java:

public class Programme
{
    public static void main(String[] arguments) {
        byte lByte = 120; // Peut prendre des valeurs entre -128 et 127.
        short lShort = -30000; // Peut prendre des valeurs entre
                               // -32768 et 32767.
        int lInt = 0; // Peut prendre des valeurs entre 
                      // -2147483648 et 2147483647.
        long lLong = 1234567890; // Peut prendre des valeurs entre
                                 // -9223372036854775808 et 9223372036854775807.
        float lFloat = 0.312f; // Nombre à virgule avec 32 bits de précision.
        double lDouble = -123.456; // Nombre à virgule avec 64 bits de précision.
        boolean lBoolean = true; // Peut prendre les valeurs `true` et `false`.
        char lChar = 'A'; // Valeur de 16 bits (comme un short) représentant un
                          // caractère Unicode. On peut préciser le code en
                          // utilisant le code hexadécimal '\u0000' à '\uFFFF'.
    }
}

Il est à noter que, techniquement, les array peuvent également être considérés comme un type primitif. En général, en langage-objet, nous préférons utiliser un type objet pour représenter les collections (comme une ArrayList par exemple). Voici un exemple d’array:

public class Programme
{
    public static void main(String[] arguments) {
        int i;
        int[] lTableau1 = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
        float[] lTableau2 = new float[10];
        System.out.println("Taille du tableau 2: " + lTableau2.length);
        System.out.println("Éléments du tableau 1:");
        for (i = 0; i < lTableau1.length; i = i + 1) {
            System.out.println("    Élément " + i + " : " + lTableau1[i]);
        }
    }
}

Les types objets

Les types objets sont des types qui sont des instances de classes. Nous ne rentrerons pas tout de suite sur ce concept, mais sachez seulement que la très grande majorité des types que vous utiliserez sont dans cette catégorie. En général, le nom de ces types débute par une lettre majuscule. Par exemple: String, Scanner, ArrayList, etc.

Il est à préciser que les types primitifs (sauf les array) ont un type objet associé. Ça permet d’utiliser une mécanique similaire au polymorphisme avec les types primitifs. Voici un exemple de variables ayant des types primitifs et étant encapsulés dans des variables ayant des types objets:

import java.util.ArrayList;

public class Programme
{
    public static void main(String[] arguments) {
        ArrayList<Object> lListe = new ArrayList<Object>();
        int lEntier = 10;
        double lNombre = 0.123;
        Integer lInteger = Integer.valueOf(lEntier);
        lListe.add(lInteger);
        lListe.add(Double.valueOf(lNombre));
        lListe.add(Boolean.valueOf(false));
        System.out.println("Élément 1: " + lListe.get(0).toString());
        System.out.println("Élément 2: " + lListe.get(1).toString());
        System.out.println("Élément 3: " + lListe.get(2).toString());
    }
}

Les structures de contrôle

Voici les principales structures de contrôles utilisés dans le langage Java:

Les conditionnelles « if »

Afin de valider une information, on peut utiliser la structure de contrôle if. Ce type de structure permet d’exécuter une branche de code précise en fonction d’une ou plusieurs conditions. Par exemple:

if (lAge >= 18) {
    System.out.println("Vous êtes majeur.");
}

Ici, on voit que la branche de code à l’intérieur de la structure if (le « print ») sera exécutée si la condition « lAge >= 18 » est vrai.

Il est également possible de préciser la branche à utiliser dans le cas ou la condition est fausse. Pour se faire, on utilise le mot clé else. Par exemple:

if (lAge >= 18) {
    System.out.println("Vous êtes majeur.");
} else {
    System.out.println("Vous êtes mineur.");
}

Il est également possible de préciser plusieurs conditions et plusieurs branches à exécutées avec une structure else if. Par exemple:

if (lAge < 18) {
    System.out.println("Yo " + lNom);
} else if(age < 60) {
    System.out.println("Salut " + lNom);
} else {
    System.out.println("Bonjour " + lNom);
}

Les boucles while

La boucle while permet d’exécuter du code à répétition, et ce, tant qu’une condition est vraie. La structure est similaire à un if (ne contenant aucun else) mais qui boucle tant que la condition est vrai. Par exemple:

import java.util.Scanner;

public class Programme
{
    public static void main(String[] arguments) {
        String lEntree = "";
        Scanner lScanner = new Scanner(System.in);
        while (!lEntree.equals("0")) {
            System.out.print("Entrez sur 0:");
            lEntree = lScanner.nextLine();
        }
    }
}

Cet exemple bouclera tant que l’utilisateur n’aura pas appuyé sur « 0 » dans la console.

Les boucles for

Les boucles for permette une itération sur un ensemble de données. La boucle for classique permet d’itérer sur l’ensemble des nombres entiers entre deux bornes. Par exemple, pour itérer entre 0 et 10, on utilise le code:

for (i = 0; i < 11; i = i + 1) {
    ...
}

Il est important de comprendre que cette version de la boucle for n’apporte rien de plus (sauf peut-être de la documentation) que la boucle while. En effet, on peut simuler la boucle for présenté plus haut par la boucle while:

i = 0;
while (i < 11) {
    System.out.println("Valeur: " + i);
    i = i + 1;
}  

De la même façon, on peut remplacer la boucle while suivante:

while (!lEntree.equals("0")) {
    ...
}

Par la boucle for suivante:

for (;!lEntree.equals("0");) {
    ...
}

Il est important de comprendre que cette boucle n’est pas acceptable dans un programme, mais elle est syntaxiquement correcte pour Java.

Les boucles for peuvent également itérer sur les éléments d’un ensemble de données. On peut par exemple parcourir les éléments d’une liste en utilisant une boucle for. Par exemple:

import java.util.ArrayList;

public class Programme
{
    public static void main(String[] arguments) {
        ArrayList<String> lListe = new ArrayList<String>();
        lListe.add("Ligne 1");
        lListe.add("Ligne 2");
        lListe.add("Ligne 3");
        lListe.add("Ligne 4");
        lListe.add("Ligne 5");
        lListe.add("Ligne 6");
        lListe.add("Ligne 7");
        lListe.add("Ligne 8");
        lListe.add("Ligne 9");
        lListe.add("Ligne 10");
        for (String element : lListe) {
            System.out.println("Element: " + element);
        }
    }
}

Retour

 


Auteur: Louis Marchand
Creative Commons License
Sauf pour les sections spécifiées autrement, ce travail est sous licence Creative Commons Attribution 4.0 International.