Java/Annexe/Oral de java


Cette page reprend les mots-clés 2013 du cours LGJ de langage JAVA au sein de l'ESI.

Oral de java
Image logo représentative de la faculté
Annexe 4
Leçon : Java

Annexe de niveau 14.

Précédent :Glossaire
Suivant :Sommaire
En raison de limitations techniques, la typographie souhaitable du titre, « Annexe : Oral de java
Java/Annexe/Oral de java
 », n'a pu être restituée correctement ci-dessus.



Considérez ces fiches comme des résumés plus ou moins exhaustifs des notions auxquelles elles font référence. Elles ont pour but d'affiner la compréhension de l'emploi des dites notions lorsque l’on fait usage du langage JAVA pour coder.

Des liens plus complets pour chaque mots-clés seront ajoutés.

Toute aide est la bienvenue.

autoboxing

modifier

Cas d’usage

modifier

Le boxing est le processus de conversion d'un type primitif en type objet. L'inverse du boxing est l'unboxing (objet vers primitif). À chaque type primitif correspond une classe englobante (wrapper) :

type primitif wrapper
boolean Boolean
byte Byte
char Character
short Short
int Integer
long Long
float Float
double Double

L'objet a la même valeur que la valeur primitive ; utile lorsqu'un objet est requis.

L'autoboxing est donc une conversion automatique d'un type primitif vers un wrapper (boxing), ou inversement, d'un wrapper vers un type primitif (unboxing). Cette conversion est ajoutée par le compilateur dans les expressions, dans les assignations et avec les paramètres effectifs.

Exemple de code

modifier
List<Integer> list = new ArrayList<>();
list.add(1);
System.out.println(list.get(0) + 1);
list.set(0, list.get(0) + 1);
int i = list.get(0);

break/continue

modifier

cas d’usage

modifier

break est un arrêt inconditionnel de l'instruction. À l'instar du saut conditionnel, il est associé à une structure conditionnelle(boucle for, while), sans laquelle la boucle ne ferait jamais plus d'un tour.

for (int x=1; x<=10; x++) {
   a = x-7;

   if (a == 0) {
      System.out.println("division par 0");
      break;
   }

   System.out.println(1.0/a);
}
  • Si pas d'étiquette → arrête la première instruction while/for/do/switch englobante.
  • Si étiquette → arrête brutalement l'instruction étiquetée et passe à la suivante.

continue

modifier

continue est un saut inconditionnel. Associé a une structure conditionnelle, il permet de passer aux instructions suivantes sans mettre fin à la boucle, ou la suite d'instruction.

x=1;

for (int x=0; x<=10; x++) {
   if (x == 7) {
      System.out.println("Division par zéro!");
      continue;
   }

   double a = 1.0/(x-7);
   System.out.println(a);
}
  • Si pas d'étiquette → recommence la première instruction répétitive englobante
  • Si étiquette → recommence la boucle étiquetée

exemple de code

modifier
code=/* Exemple d'utilisation de while */

int nb;
while(true) { 
    nb = clavier.nextInt();
    if (nb>0) break;
    System.out.println("Mauvais nombre. Recommencer.");
}

/* Exemple d'utilisation de continue */

for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue;
    System.out.println(i);
}

constructeur

modifier

cas d’usage

modifier

Un constructeur est une méthode qui sert à créer une instance d'un objet. Il ne retourne rien et a le même nom que celui de la classe.

Il existe deux types de constructeurs :

  • le constructeur par défaut, sans paramètre ;
  • le constructeur avec un ou plusieurs paramètres, permettant d'attribuer directement les valeurs des paramètres aux attributs.

Il peut ne pas y avoir de constructeur explicite : rarement une bonne idée → toujours écrire explicitement un constructeur.

exemple de code

modifier
public Etudiant (int unNuméro, String unNom) {
    numéro = unNuméro;
    nom = unNom;
    annéeÉtude = 1;
    doubleur = false;
    ancien = false;
}

conversions

modifier

cas d’usage

modifier

(la partie qui suit n'a pas vraiment de structure, j’ai simplement rassemblé les informations trouvées dans le cours théorique)

On ne peut normalement pas mélanger des variables de types différents. Cela est cependant accepté s'il n'y a pas de perte d'information. La conversion est effectuée automatiquement par le compilateur.

Java étant un langage fortement typé, les types doivent correspondre entre eux. Lors d'un assignation par exemple, la valeur doit être du type de la variable. Il y a 8 sortes de sortes de conversions, qui peuvent être utilisées dans 5 contextes différents. Il existe des conversions implicites dans les expressions :

  • Uniquement pour des expressions numériques
  • Adapte le type des opérandes à ceux attendus par l’opérateur
  • Conversion la plus fréquente : élargissante de type primitif

Conversion élargissante de type primitif (widening primitive conversion) :

  • Vers un type plus général : byte → short → int (← char) → long → float → double
  • Réel vers entier : perte de précision possible

Cas des opérandes binaires :

  • Opérateurs : +, -, *, /, %, <, >, <=, >=, ==, !=
  • Si opérandes de types différents : on élargit le moins large
  • On élargit au minimum vers int (car les opérateurs n'existent pas en dessous)

Cas des opérateurs unaires :

  • Opérateurs : +, -, ainsi que l'indice d'un tableau
  • byte, short, char → int

Remarque sur ++ :

  • Existe pour tous les types numériques
  • Attention, sur un char par exemple : a++ est différent de a ← a + 1

Que se passe-t-il lors d'une assignation ?

  • Adapte le type de l’expression au type de la variable
  • Opérateurs : =, +=, -=, *=, /=, %=
  • Permet la conversion élargissante et arrondissante.

encapsulation

modifier

cas d’usage

modifier

L'encapsulation est le concept d'accessibilité appliqué aux méthodes et aux classes. Elle permet au concepteur de restreindre ou non l'accès a certaines méthodes/classes. la visibilité est définie par les mots clés public, private et protected.

L'encapsulation est associé au concept des mutateurs et des assesseurs. Pour permettre l'usage d'une méthode private par des méthodes externes, ils sont indispensables.

L'encapsulation permet donc de protéger son code tout en prévoyant des outils pour en faire usage.

cas d’usage

modifier

Une énumération est un ensemble fixe et petit de valeurs ayant un nom et étant sémantiquement liées.

Le mot-clé enum :

  • définit un nouveau type de données ;
  • est au même niveau que class ;
  • est défini dans un fichier à part ou au sein d'une autre classe.

On peut par exemple accéder aux différentes valeurs d'une classe enum grâce à un switch, une boucle for, ou encore un foreach.

On peut également y ajouter des attributs ou ses propres méthodes.

exemple de code

modifier
public enum Saison {
    PRINTEMPS, ÉTÉ, AUTOMNE, HIVER;
}

cas d’usage

modifier

La méthode equals compare deux objets et renvoie un booléen selon que ceux-ci sont égaux (dans le même état) ou non.

La méthode par défaut dans Object se contente de comparer les références (les objets sont des types références) → besoin de la réécrire :

  • Il faut respecter la signature.
  • La méthode doit renvoyer false si on compare à un autre type ou à null.

Elle est utilisée notamment dans les listes, dans les méthodes contains, indexOf, remove, etc.

cas d’usage

modifier

Le mot-clé final en java peut s’utiliser de différentes façons; en générale il est là pour dire que la chose à laquelle il est lié ne peut pas changer.

  • final pour variable : si on déclare une variable final, elle ne pourra pas changer de valeur. Tout le long de l’exécution du programme, cette variable gardera la valeur de son initialisation. Cette variable doit être primitive et doit être initialisée avec une valeur. Cette variable sera donc constante. Si on utilise final sur une référence, celle-ci ne pourra pas référencer un autre objet, la référence sera liée à l’objet. L’objet référencé peut par contre, changer de valeur.

Si on n’initialise pas une variable final, on est obligé de le faire avant d’utiliser cette variable.

  • final pour un argument : cela veut dire que la méthode qui reçoit l’argument final ne pourra changer ce vers quoi il pointe.
  • final pour méthode : permet que l’on ne puisse redéfinir la méthode. Une autre classe ne pourra donc pas la ré implémenté. Cela permet d’être certain que le comportement de cette méthode ne changera pas durant l’héritage (empêche la surcharge).
  • final pour une classe: empêche de pouvoir hériter de cette classe. On peut choisir de rendre une classe final pour des raisons de sécurité. Cette classe ne pourra donc pas être modifiée.

exemple de code

modifier
final int a = 9 ; // déclaration d'une variable final
    a=10 ; // On ne pourra donc pas écrire ceci : cela produit une erreur de compilation.

maMéthode( final int b) { // utilisation d'argument final
    b = 5 ; // ne compilera pas interdit !!! 
    return b ; 
}

cas d’usage

modifier

La boucle for teste une condition et exécute les instructions qui la compose tant que cette condition n’est pas remplie. On l'utilise généralement pour parcourir des listes ou des tableaux.

Contrôles de la boucle en tête de boucle pour une meilleure lisibilité :

  1. l'initialisation
  2. l'évaluation du test
  3. si le test vaut true, alors :
    1. le corps
    2. incrémentation (au sens large)
    3. retour à l'étape 2
  4. sinon, on passe à l'instruction suivant le for

Utilisé en général quand le nombre d'itérations est connu.

exemple de code

modifier
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

foreach

modifier

Écriture simplifiée du for. Utile pour parcourir un tableau, une liste ou un enum.

Attention ! On accède aux éléments mais pas à l'indice ! → OK pour consulter mais pas pour modifier.

for (String mot: dictionnaire){
    System.out.println(mot);
}

cas d’usage

modifier

I/O désigne Input/Output ou Entrées/Sorties.

  • Les Entrées sont l’ensemble des périphériques, fichiers, ou objets permettant de recevoir des données à la machine.

Exemple : Clavier, souris, tablette tactile, scanner de badge, fichier, objet, …

  • Les Sorties sont l’ensemble des périphériques permettant de sauvegarder/afficher des données.

Exemple : Écran, imprimante, fichier, objet, …

L’entrée-sortie peut-être de type :

    • binaire (byte) ou texte (char)
    • séquentiel, direct, . . .

On peut y accéder en lecture, écriture, lecture/écriture, . . .
On peut l’envisager à partir de / vers un fichier mais aussi d’un tableau en mémoire, socket, autre application.

Les différent flux sont :

    • Flux standard
    • Flux binaire
    • Flux de caractère (texte)
    • Flux englobant
    • Flux formaté
    • Flux de type primitif

exemple de code

modifier
public void sauver(String nomFichier) {
    File saveFile = new File(nomFichier);
    this.lastParties = new Stack<Partie>();
    try {
        FileOutputStream out = new FileOutputStream(saveFile);
        ObjectOutputStream save = new ObjectOutputStream(out);
        lastParties.push(puissance4); save.writeObject(lastParties);
        save.close();
        out.close();
    } catch (IOException e) {
    }
}

Voir aussi : java.io

i++/++i

modifier

cas d’usage

modifier

++ permet d'incrémenter une variable. Il peut se placer avant ou après la variable.

Il y a cependant une différence :

  • pour ++i, i est incrémenté avant d’être utilisé :
    • i est incrémenté
    • i donne sa valeur à l’expression ++i
  • pour i++, i est incrémenté après avoir été utilisé :
    • i donne sa valeur à l’expression i++
    • i est incrémenté

exemple de code

modifier
int i = 5;
int a;
a = i++ + ++i; // 5 + 7
System.out.println("a vaut : "+a); // 12
System.out.println("i vaut : "+i); // 7

Détaillons a = i++ + ++i si i = 5 :

  • On est face à une assignation. Il faut donc évaluer l’expression à droite puis la donner à a.
  • L'expression à évaluer est une somme. Il faut donc évaluer les 2 opérandes i++ et ++i. Dans quel ordre ? Java évalue toujours l'opérande de gauche PUIS celle de droite ;
  • Il faut donc évaluer i++. Cette expression a une valeur 5 et on incrémente i (qui vaut 6) mais l'évaluation est faite avant, i++ vaut donc 5 ;
  • On évalue à présent ++i. D'abord on incrémente et i passe à 7. On évalue alors i donc ++i vaut 7 ;
  • Les 2 opérandes étant évalués, on peut faire la somme du point 2 : 5 + 7 = 12 ;
  • l’expression à droite de l'assignation vaut donc 12, et on peut terminer le point 1 : a reçoit 12 ;
  • On a donc bien a = 12 et i = 7.


Dis autrement:

  • a = i++ + ++i
  • a = ( (i++) + (++i) ) // pour montrer comment les priorités induisent les parenthèses
  • a = ( (5) + (++i) ) // i est utilisé puis passe à 6
  • a = ( (5) + (7) ) // i passe à 7 puis est utilisé
  • a = 12.

cas d’usage

modifier

Le if est associé à une condition. Si celle-ci est remplie, le code contenu dans le if est exécuté, sinon on en sort et ce qui suit est exécuté. Il peut être suivi par une instruction, pas forcément un bloc.

Le if peut être suivi d'un else, ou encore d'un else if.

exemple de code

modifier
public static void afficheTableau(int[] tableau) {

    if (tableau == null) {
        throw new IllegalArgumentException("pas de tableau !");
    }

    if (tableau.length() > 0) {
        System.out.println ("Contenu du tableau : ");
        for (int i = 0; i < tableau.length(); i++) {
            System.out.println("en "+ i + " : " + tableau[i]);
        }
    } else {
        System.out.println("tableau vide");
    }
}

implements

modifier

cas d’usage

modifier

implements permet d'implémenter une interface : définir toutes les méthodes d'une interface.

On déclare qu'on implémente l'interface. Le compilateur vérifie ensuite qu'on fournit bien le code de chaque méthode.

exemple de code

modifier
public class MaClasse implements MonInterface {
    public void maMéthode1(int a) {
        // le corps de la méthode
    }

    public boolean maMéthode2(char c) {
        // le corps de la méthode
    }

    // + d'autres méthodes si on veut
}

Pour utiliser une classe existante, on peut soit mettre le nom qualifié (complet), soit utiliser import qui crée un raccourci.

Cas particulier : le package java.lang est importé implicitement. On peut donc utiliser directement les classes qui le constituent.

import java.util.Calendar;

public Test {

    // ...
    Calendar now = Calendar.getInstance();
    // ...
}

instanceof

modifier

L'opérateur instanceof permet de vérifier qu'un objet appartient à une classe donnée (ou un de ses enfants). Par définition, null n'est instance de rien.

public boolean equals(Object o) {
    if (!(o instanceof Etudiant)) {
        return false;
    }
    // ...
}

instruction-expression

modifier

cas d’usage

modifier
  • Une instruction représente les ordres donnés pour l’exécution du code, soit presque chaque ligne du code.
  • Une instruction peut être :
    • une affectation : x = 1 ;
    • un ordre d'affichage : systemOut.println() ;
    • une conditionnelle : if... else... ;
    • une boucle : for... while... .
  • Une expression est un ensemble de symbole représentant un calcul ayant une valeur et un type.
  • Une expression peut être :
    • une variable : x ;
    • une expression arithmétique : 3/4 ;
    • une expression booléenne : true ou x<10 ;
    • une chaine de caractère ;
    • un appel a une fonction : nomClass.nomMethod().
  • Une instruction peut être une expression, et vice-versa. Si une expression n’est pas une instruction, alors elle ne s'utilise qu'au sein d'une instruction, et pas toute seule.

exemple de code

modifier
x+3 ; // est une expression, cette instruction est donc fausse.
x = 3 + 5 ; // est une instruction-expression.

cas d’usage

modifier

Une liste est une séquence d'éléments (ordonnés mais pas nécessairement triés) auxquels on accède via leur position.

Ce concept est présent en Java, pas dans le langage, mais via l'API standard : java.util.ArrayList<E> ou java.util.LinkedList<E>.

On spécifie le type des éléments (via les <>). Au départ, la liste est vide, mais elle pourra évidemment grandir au besoin (pas de limite). Le premier élément est en position 0 ; une insertion provoque un décalage des éléments suivants. Les éléments ne peuvent pas être de type primitif, on doit donc recourir à des wrappers (enveloppes). Quelques méthodes de l'interface List :

Informations sur l'état de la liste
int size(); Récupère le nombre d'éléments dans la liste
boolean isEmpty(); Permet de savoir si la liste est vide
boolean contains (Object o); Permet de savoir si la liste contient l’objet o
Object get (int i); Renvoie l’objet se trouvant à l'indice i
Agir sur les éléments de la liste
void add (Object o); Ajoute l’objet o en fin de liste
void add (int i, Object o); Ajoute l’objet o à l'indice i
void set (int i, Object o); Remplace l’objet de l'indice i par l’objet o
Object remove (int i); Retire l’objet de l'indice i et le renvoie
boolean remove (Object o); Retire l’objet o de la liste et renvoie true ou false si l’objet n'existe pas dans la liste
void clear(); Vide complètement la liste

exemple de code

modifier
/* Déclaration */

ArrayList<String> liste = new ArrayList<>(); // depuis Java 7, plus besoin d'indiquer le type dans le membre de droite

/* Ajout d'éléments */

liste.add(E e); // ajoute l'élément 'e' en fin de liste
liste.add(int indice, E e); // ajoute l'élément 'e' à l'indice 'indice'

/* Taille */

liste.size(); // donne la taille de la liste
liste.isEmpty(); // indique si elle est vide

/* Accès */

liste.get(int indice); // renvoie l'élément se trouvant à l'indice 'indice'

/* Remplacement */

liste.set(int indice, E e); // remplace l'élément 'e' se trouvant à l'indice 'indice'

/* Recherche */

boolean contientE = liste.contains(E e); // indique si l'élément 'e' est présent
int indice = liste.indexOf(E e); // renvoie l'indice de la première occurrence de l'élément dans la liste

/* Suppression */

liste.remove(int indice); // enlève l'élément se trouvant à l'indice 'indice'
liste.remove(E e); // enlève l'élément 'e'

/* Parcours */

for (int i = 0; i < liste.size(); i++) { // for "normal"
    System.out.println(liste.get(i));
}

for (String mot : liste) { // foreach
    System.out.println(mot);
}

méthode

modifier

Une méthode est l'équivalent du module du langage de description d'algorithmes.

Une méthode permet de découper le code en plusieurs parties afin de :

  • le réutiliser
  • scinder la difficulté
  • faciliter le déverminage
  • accroitre la lisibilité
  • diviser le travail

Comment ?

  • Elle a un nom qui décrit tout ce que le code fait
  • Elle résout un sous-problème bien précis
  • Elle est fortement documentée
  • Elle est la plus générale possible
  • Elle tient sur une page

Une méthode est une sorte de boite noire : elle reçoit des données, et renvoie un résultat.

Pour l’utiliser, on doit savoir :

  • Son nom
  • Quoi lui donner
  • Ce qu'elle retourne
  • Mais pas comment elle fait

Pour appeler une méthode :

  • À partir du code d'une autre classe : NomClasse.nomMéthode(...)
  • Si on est dans la même classe : on indique directement le nom de la méthode

Il est essentiel de commenter chaque méthode :

  • Pour savoir pourquoi et comment l’utiliser
  • Pour la comprendre et ainsi pouvoir la corriger et/ou la modifier

On utilise pour cela la documentation (Javadoc)

Passage de paramètres : En logique, 3 passages de paramètres : en entrée, en sortie, en entrée-sortie. En Java, uniquement par valeur :

  • la valeur est copiée dans le paramètre
  • ≈ paramètre en entrée

Conclusion :

  • Une méthode fait une et une seule chose
  • Une méthode possède un nom explicite
  • Une méthode est fortement documentée

cas d’usage

modifier

L’opérateur new sert à instancier une classe. C’est-à-dire créer un nouvel objet .

Pour créer cet objet, on fait appel à une méthode spéciale de la classe : le constructeur.

Généralisation : [class] [nom du nouvel objet] = new [class]([arguments du constructeur])

exemple de code

modifier
Scanner clavier = new Scanner(System.in)

cas d’usage

modifier

un objet est une donnée structurée contenant des attributs et des fonctionnalités.

Pour effectuer des opérations sur un objet, on fait appels à des méthodes de l'objet. les opérateurs == et !=, à l'instar des primitifs, sont utilisables sur un objet.

un objet dispose de 2 types de méthodes : getters/setters // accesseurs/mutateurs. get : récupérer les attributs d'un objet ; renvoi une valeur. set : modifier les attributs d'un objet ; méthode void le plus souvent; peut renvoyer un booléen.

Pour fonctionner, certaines méthodes utilisent des paramètres d'appels.

==//!= permettent uniquement de vérifier si deux variables objets référencent le même objet. Pour vérifier si ces deux variables objets ont le même contenu, il faut faire appel à la méthode equals.

package

modifier

L'API désigne la bibliothèque standard Java. Elle contient des milliers de classes qui sont regroupées en packages.

Un package

  • regroupe les classes liées
  • permet l'unicité des noms de classe (nom complet/qualifié : package.Classe)

Nom d'un package :

  • identifieurs séparés par des points
  • tout en minuscules
  • généralement adresse internet inversée (unicité) :
    • be.heb.esi.java1, org.junit.junitCore

Pour utiliser une classe d'un package donné, soit on indique le nom qualifié, soit on utilise import qui crée un raccourci (voir import).

Pour créer une classe dans un package, on indique package nom_package; en tant que première instruction du fichier.

Scanner

modifier

cas d’usage

modifier

La classe Scanner permet d'analyser des types primitifs et des chaines de caractères. Un objet de type Scanner découpe en tokens ce qu’il reçoit en entrée, en utilisant un délimiteur (par défaut, l'espace).

On peut par exemple lire un entier depuis System.in (généralement le clavier) en utilisant la méthode nextInt(), un token en utilisant next(), etc.

Les méthodes hasNextInt(), hasNext(), hasNextLine(), etc. permettent de vérifier que le token en entrée peut bien être interprété comme un entier, un token, une ligne, etc.

exemple de code

modifier
public static void lireClavier() {
    Scanner clavier = new Scanner(System.in);
    int nombre;
    String mot;
    String phrase;

    nombre = clavier.nextInt();
    mot = clavier.next();
    phrase = clavier.nextLine();
}

Serializable

modifier

Une classe doit implémenter Serializable pour être sérialisable.

  • Pas de méthode, sert juste de tag
  • Tous ses attributs doivent aussi être sérialisables

La sérialisation est utilisée pour

  • la communication réseau entre processus Java
  • la sauvegarde des données d'une application :
    • Pas très efficace → tout est lu ou sauvé en bloc
    • La classe ne doit pas avoir changé entre le moment de l'écriture et le moment de la relecture → problème de pérennité de l'information

Le mot-clé static s'applique aux membres (attributs et méthodes)

  • N'est plus un membre de l’objet (instance de la classe) mais un membre de la classe
  • Est partagé par toutes les instances

Un attribut statique

  • existe en un seul exemplaire
  • est initialisé lors du chargement de la classe (une seule fois)
  • utilisation courante : constantes

Une méthode statique

  • ne peut pas accéder aux membres des instances
  • utilisation courante : méthodes non objets

À l'extérieur de la classe, on préfixe par le nom de la classe ou par un objet de la classe (non recommandé).

import static crée un raccourci pour l'accès aux membres statiques.

super est lié à l'héritage.

  • super(param1, param2) appelle le constructeur de la classe parente, ou superclass
  • super.attribut accède à l'attribut de la superclasse
  • super.méthode() accède à la méthode de la superclasse

cas d’usage

modifier

Le switch est proche du selon que utilisé en logique.

  • L'expression à évaluer est de type limité : char, byte, short, int ou String
  • Les expressions constantes sont toutes différentes
  • On utilise un break pour terminer un case
  • On peut omettre le default, mais s'il apparait, il doit être unique (et mis à la fin de préférence)
  • Les blocs sont admis mais pas recommandés

Le switch ne permet pas de traduire le selon-que avec condition. On utilise alors if - else if.

exemple de code

modifier
switch (jour) {
    case 0: System.out.println("Lundi"); break;
    case 1: System.out.println("Mardi"); break;
    case 2: System.out.println("Mercredi"); break;
    case 3: System.out.println("Jeudi"); break;
    case 4: System.out.println("Vendredi"); break;
    case 5: System.out.println("Samedi"); break;
    case 6: System.out.println("Dimanche"); break;
    default: System.out.println("Erreur");
}

tableau à 1 dimension

modifier

un tableau tab[n] est une variable pouvant contenir n données de types primitifs ou Objets. chaque donnée est référencé a l'indice n-1 dans le tableau. il est indispensable au parcours du tableau avec une boucle for. pour une boucle foreach, le type de donnée est suffisant. Un tableau peut s'utiliser en paramètre d'une fonction.

    final int MAX = 10;
 
    int[] list = new int[MAX];
 
    // Remplit le tableau
    for (int i = 0; i < MAX; i++)
    {
        list[i] = i * 10;
    }
 
    // On change la quatrième valeur
    list[3] = 999;
 
    // On affiche le contenu du tableau
    for (int i = 0; i < MAX; i++)
    {
        System.out.print (list[i] + " ");
    }


tableau à 2 dimensions

modifier

un tableau tab[n,m] à 2 dimensions peut être considéré comme un tableau contenant des références vers des tableaux. chaque donné peut être retrouvé grâce à son indice dans le tableau n, puis son indice dans le tableau m.

int[][] liste = {{1, 2, 3}, {4, 5, 6}};
 
for (int i = 0; i < liste.length; i++)
{
    for (int j = 0; j < liste[0].length; j++)
    {
        System.out.print (liste[i][j] + " ");
    }
    System.out.println();
}

tests unitaires

modifier

Les tests unitaires sont des tests de chaque méthode.

  • Fait-elle ce qu'elle est sensée faire ?
  • C'est défini par la spécification (documentation)
  • Idée : si chaque méthode est correcte → le tout est correct
  • Pas forcément suffisant (exemple : ne teste pas la performance) → d'autres types de tests existent

Plan de tests

modifier

Tester une méthode ne s'improvise pas.

  • Besoin d'un plan reprenant les tests à effectuer
    • Quelles valeurs de paramètres ?
    • Quel est le résultat attendu ?
  • Préparé pendant que l’on code (ou même avant et éventuellement par une autre personne)
  • Permet de s'assurer que l’on teste tous les cas

On ne peut pas tester toutes les valeurs possibles.

  • Choisir des valeurs représentatives
    • Cas généraux / particuliers
    • Valeurs limites
  • Il faut imaginer les cas qui pourraient mettre en évidence un défaut de la méthode

On s'inspire des erreurs les plus fréquentes en programmation.

  • On commence/arrête trop tôt/tar une boucle
  • On initialise mal une variable
  • Dans un test, on se trompe entre < et ≤
  • Dans un test, on se trompe entre && et ||
  • ...

C'est un savoir-faire → importance de l'expérience

Quand tester ? Le plus souvent possible.

  • Erreur plus facile à identifier/corriger
  • Idéalement après chaque méthode écrite

Que tester ? Tout.

  • Le nouveau code peut mettre en évidence un problème dans le code ancien (régression)

Comment tester ? JUnit (par exemple).

  • Le programmeur fournit les tests ;
  • JUnit exécute tous les tests et ;
  • établit un rapport détaillant les problèmes

En pratique

modifier

La classe de test contient une méthode de test par cas.

  • Autonome (ne reçoit rien ni ne retourne rien)
  • Contient des affirmations
    • Appel de la méthode à tester
    • Comparaison entre le résultat attendu et le résultat obtenu

Comment lancer les tests ?

java org.junit.runner.JUnitCore ClasseTest

Résultat ?

  • Nombre de (méthodes de) tests effectués / réussis
  • Détails sur les tests ratés :
    • Nom du test
    • Résultat obtenu comparé au résultat attendu

On teste aussi les exceptions.

/* Exemple de code à tester */

package be.heb.esi.java1;

public class Outil {
    public static int max(int[] tab) {
        int max = 0;

        if (tab == null) {
            throw new IllegalArgumentException("Le tableau ne peut être vide");
        }

        for(int i=0; i<tab.length; i++) {
            if (tab[i]>max) {
                max = tab[i];
            }
        }

        return max;
    }
}

/* Exemple de classe de test */

package be.heb.esi.java1;

import org.junit.Test; // Pour que @Test soit connu
import static org.junit.Assert.; // Pour assertEquals

public class OutilTest {
    @Test
    public void max_cas1() {
        int[] tab = {1, 3, 0, 2};
        assertEquals(3, Outil.max(tab));
    }

    @Test
    public void max_cas2() {
        int[] tab = {1, 3, 4, 2};
        assertEquals(1, Outil.max(tab));
    }

    @Test(expected=IllegalArgumentException.class)
    public void max_cas_null()
        int[] tab = null;
        Outil.max(tab);
    }

    // etc.
}

Le mot-clé this est une référence à soi-même. Il est implicite lors d'une utilisation directe du membre.

Règle : un paramètre/une variable locale masque un attribut. this permet d'accéder à l'attribut masqué.

Certains l'utilisent systématiquement pour une meilleure lisibilté.

this peut être utilisé lorsque les constructeurs d'une même classe se ressemblent. C'est plus facile si un constructeur appelle l'autre. this doit alors être la première instruction.

/* Constructeurs de la classe Étudiant */

public Étudiant(int numéro, String nom, int année, boolean doubleur, boolean ancien) {
    this.numéro = numéro;
    this.nom = nom;
    this.année = année;
    this.doubleur = doubleur;
    this.ancien = ancien;
}

public Étudiant(int numéro, String nom) {
    this(numéro, nom, 1, false, false);
}

throw, à ne pas confondre avec throws, permet de lancer une exception. Toutes les exceptions sont de type java.lang.Throwable.

throw nécessite un seul argument : un objet lançable.

public static double racineCarrée(double nombre) {
    if (nombre < 0) {
        throw new IllegalArgumentException("Le nombre doit être positif");
    }
    // ...
}

Si une méthode est capable de lancer une exception qu'elle ne gère pas, elle doit le préciser afin que les appelants de la méthode puissent se prémunir contre cette exception. On réalise cela en incluant la clause throws dans la déclaration de la méthode. Cette clause liste les types d'exceptions que la méthode peut lancer. Si celles-ci ne sont pas déclarées, une erreur de compilation se produira.

Si la méthode lance plusieurs exceptions, on les déclare en les séparant par des virgules.

public void f() throws IllegalArgumentException, IOException {

    // ...
    if (...) {
        throw new IllegalArgumentException("...");
    } else if (...) {
        throw new IOException("...");
    }
    // ...
}

toString

modifier

toString() est une méthode de la classe java.lang.Object. Elle renvoie une représentation textuelle de (l'état de) l'objet.

Par défaut, elle renvoie une chaine de caractères comprenant le nom de la classe de laquelle l’objet est instancié, le caractère @ et la représentation non-signée hexadécimale du hash code de l'objet.

Il est conseillé de la réécrire dans toutes les sous-classes.

public class Personne {

    private String nom;
    private String prénom;

    public Personne(String nom, String prénom) {
        this.nom = nom;
        this.prénom = prénom;
    }

    @Override
    public String toString() {
        return "Nom : " + this.nom + "\nPrénom : " + this.prénom;
    }
}

try-catch

modifier

généralement associé a catch, try permet de tester un bout de code susceptible de créer une Exception, et de gerer celle-ci grâce au catch, ou multi-catch. Si le catch ne reprend pas l'Exception levé par le try, le programme s'arrete avant de reprendre son cours. Si le catch s'exécute, le programme reprend son cours après le bloc try-catch. le try peut aussi s'utiliser avec finally; le catch n'est alors pas indispensable.

type primitif vs référence

modifier

Toute donnée a un type :

  • Cohérence sémantique
  • Allocation mémoire adaptée

Différents types :

  • primitifs prédéfinis : booléen, entier, réel
  • références prédéfinis : tableaux, String, etc.
  • références définis par le programmeur

Allocation mémoire :

  • type primitif
    • Indique la zone mémoire (sur la pile/stack) où se trouve la valeur
  • type référence
    • La zone mémoire contient l'adresse de la zone mémoire (sur le tas/heap) contenant la valeur (indirecction)

La méthode pour déterminer si deux valeurs sont égales est différente selon que ce soit un type primitif (les valeurs sont comparées) ou un type référence (les références sont comparées).

types primitifs et conversions

modifier

Une conversion peut être implicite par affectation ou par promotion arithmétique. Lorsqu'elle est explicite, on fait appel a un opérateur de cast. Exemple: float f = (float) x; //cette conversion explicite étant sans perte, elle pourrait se faire par simple affectation.


Conversions sans perte :

De Vers...
byte short, int, long, float ou double
short int, long, float ou double
int long, float ou double
long float ou double
float double

Conversions avec perte :

De Vers...
short byte ou char
char byte ou short
int byte, short, ou char
long byte, short, char ou int
float byte, short, char, int ou long
double byte, short, char, int, long ou float


visibilité (private, ...)

modifier

En Java, 4 visibilités :

  • public : visible dans toutes les classes → public
  • privé : accessible uniquement dans la classe où le membre est utilisé → private
  • paqueté : visible dans toutes les classes du package
  • protégé : accessible uniquement dans le package où il se trouve (pas vu en première) → protected

Rappel de bonne pratique : attributs privés / méthodes publiques