IPRO - TP/Projet
Guillaume Bouyer
ENSIIE

#1. Prise en main de Java

#1.1. Java SDK (d'après Gauthier Picard, EMSE)

Le SDK regroupe un ensemble d'outils permettant principalement de compiler et d'exécuter des programmes Java. Actuellement, la dernière version stable est Java 8. Elle est téléchargeable sur le site Java SE d'Oracle.

Java est un langage interprété, ce qui signifie qu'un programme compilé n'est pas directement exécutable par le système d'exploitation mais il doit être interprété par un autre programme, qu'on appelle interpréteur.

jvm

#1.1.1. Le compilateur javac

javac est le compilateur fourni dans le SDK Un code source .java est compilé en un langage appelé bytecode dans un fichier .class.

javac MaClasse.java

#1.1.2. L'interpréteur java et la machine virtuelle

Le bytecode n'est pas directement exécutable. Il doit être interprété par une machine virtuelle Java (la JVM) qui le traduit en un langage adapté au système d'exploitation. Le SDK fournir cette machine virtuelle pour interpreter une classe compilée.

java MaClasse

NB : l'extension .class ne doit pas être précisée dans l'instruction d'interprétation.

#1.1.3. Séparation des sources et des classes compilées

Afin de mieux structurer les différentes parties du programme les fichiers contenant le code source des classes sont souvent séparés des fichiers contenant le bytecode Pour créer les fichiers compilés dans répertoire particulier : -d

javac -d /dossierbytecode MaClasse.java

Lorsque le bytecode ne se situe pas dans le répertoire courant, il faut signaler à java son emplacement par l'option classpath.

java -classpath /dossierbytecode MaClasse

#1.2. Exercice

#1.2.1. Question 1

Créez un répertoire tp1, puis 2 répertoires nommés src et bin.

Ouvrez un éditeur de texte, puis créez un nouveau fichier nommé HelloWorld.java dans le répertoire tp1/src.

Ecrivez le code source d'une classe HelloWorld qui contient :

Compilez cette classe de manière à générer le bytecode dans bin

javac -d bin src/HelloWorld.java

#1.2.2. Question 2

La classe HelloWorld n'est pas exécutable dans son état actuel. Une classe exécutable est une classe qui contient une méthode spécifique (main) utilisée comme point de départ de l'exécution.

public static void main(String[] args){
   //...
}

Le tableau args de la méthode main contient tous les arguments passés en paramètres du programme Java.

Ajoutez une méthode main à la classe HelloWorld qui va :

Compilez comme précédemment.

#1.2.3. Question 3

Exécutez la classe HelloWorld grâce à la commande suivante :

java -classpath bin HelloWorld

Rq : Lors des erreurs d'exécution, java affiche la trace de la pile d'appels qui ont amené à cette erreur. Cette liste d'appels est affichée de l'appel le plus interne à l'appel le plus externe.


#2. Prise en main d'Eclipse

#2.1. IDE Eclipse

Eclipse est un IDE (Integrated Development Environment) multi-plateforme, utilisable pour plusieurs langages.

eclipse

Vous trouverez de nombreuses présentations ou tutoriels de prise en main. Seuls les points d'usage nécessaires au démarrage sont donnés ici.

Dans l'exemple de disposition dans la figure vous noterez : le raccourci vers la “perspective java” (haut droite), l'outline (liste des membres de la classe), la sortie console et la liste des éventuels problèmes de compilation

#2.1.1. Workspace

Le “workspace”, dont l'emplacement vous est demandé au démarrage, sert à Eclipse à stocker les informations relatives à vos projets (configurations…).

#2.1.2. Gestion du projet

Pour créer un projet : New -> Java Project

Le projet apparait dans la vue “package explorer”. En cas de mauvaise manipulation vous pouvez supprimer un projet sans supprimer les fichiers du disque, et recommencer. De nombreuses options sont disponibles avec un clic droit sur le projet :

Ces commandes peuvent être accessibles sous une forme identique ou personnalisée si vous cliquez droit sur un dossier du projet, un package ou une classe. Concernant les classes, on retiendra :

#2.1.3. Compilation

La compilation est faite en continu automatiquement par eclipse. Les erreurs sont soulignées en rouge, et des corrections sont également proposées

eclipsebug

NB : Lisez attentivement les messages (comme en compilation console) et n'acceptez pas la 1ère correction proposée sans réfléchir (cf exemple ci-dessus). De plus, eclipse signale parfois des erreurs imaginaires (souvent un problème d'indexation des fichiers ou de non-sauvegarde).

#2.1.4. Exécution

Menu Run -> Run, ou ctrl+F11, ou bouton vert triangle blanc, ou clic droit -> run as

Il faut au moins 1 main(), s'il y en a plusieurs il faut en choisir 1 au lancement

Le résultat de l'exécution s'affiche dans la console.

#2.1.5. Conclusion rapide

Un IDE fait gagner du temps s‘il est maitrisé, peut en faire perdre sinon, mais masque également une partie du fonctionnement bas niveau (ex. compilation). Il est donc important de le pratiquer, de bien comprendre son fonctionnement, et de garder les bases de java à l'esprit pour ne pas être totalement dépendants de l’éditeur.

#2.2. Exercice

Importez le programme précédent sous Eclipse et testez.


#3. Personnes

#3.1. Exercice 1

#3.2. Exercice 2

#3.3. Exercice 3 : documentation

#4. Matériel

Un établissement d'enseignement possède du matériel pour faire des TP.

On se limitera ici aux téléphones et aux webcams.

Les téléphones sont décrits par (pour simplifier) :

Les webcams sont décrites par :

#4.1. Exercice 1

  1. Créez un package devices.

  2. Proposez une hiérarchie de classes pour représenter les 2 types de matériels (et d'autres à venir).

  3. Après discussion, implémentez le contenu des différentes classes (vous pouvez maintenant utiliser les fonctionnalités eclipse lors de la création des classes, puis le menu Source) :

    • attributs
    • constructeurs
    • get/set utiles
    • toString() (cf doc)
  4. Ecrivez une classe principale DevicesDemo pour tester la création et l'affichage de quelques matériels.

  5. Ajoutez les méthodes equals() de test d‘égalité structurelle et testez l’égalité de quelques matériels en se basant sur leur nom (cf doc).

#4.2. Exercice 2

Un fichier .jar est une archive exécutable d'un projet java.

#5. Employés

On souhaite programmer le calcul des salaires hebdomadaires des employés d'une entreprise. Cette entreprise comporte plusieurs types d'employés :

Question :

Dans le package people

  1. Créez une hiérarchie de classes pour représenter les employés
    • Le calcul des salaires se fera dans une méthode getSalary() qui sera utilisée pour faire du polymorphisme.
    • utilisez eclipse pour générer rapidement les nombreux get et set
  2. Créez une classe principale EmployeeDemo qui :
    • crée les employés et les enregistre dans un tableau.
    • affiche le salaire hebdomadaire de chacun des employés sous la forme : “Dupond earns 2500 € ”. (Vérifiez les calculs des salaires…)

#6. Figures 2D

Créez un nouveau projet Figures

#6.1. Points 2D

  1. Ecrire une classe Point2D, avec des attributs encapsulés (getX, getY…)
  2. Ajouter des constructeurs (par défaut, valué, copie)
  3. Définir la méthode translate dans le plan 2D
  4. Ajouter une méthode pour calculer la distance d’un point à un autre (la racine carrée se calcule avec la méthode sqrt du package Math)
  5. Ajouter une méthode toString
  6. Ecrire une classe FiguresDemo qui crée quelques points, les manipule et les affiche

#6.2. Cercle

  1. Ecrire une classe Circle possédant un centre et un rayon
  2. Ajouter des constructeurs (par défaut, valué, copie)
  3. Définir la méthode translate dans le plan 2D
  4. Ajouter une méthode qui teste si un point est contenu dans le cercle courant
  5. Ajouter une méthode toString
  6. Ajouter la méthode qui retourne le périmètre
  7. Puis celle qui retourne l'aire

#6.3. Figure

On veut pouvoir manipuler (translater, calculs…) plusieurs figures de formes différentes.

  1. Implémentez une solution.
  2. Modifiez Circle en conséquence
  3. Testez dans FiguresDemo

#6.4. Rectangle

  1. Ecrire une classe Rectangle caractérisé par son coin inférieur gauche, sa longueur et sa largeur
  2. Définir un constructeur qui prend en paramètre les coordonnées du point inférieur gauche, la longueur et la largeur
  3. Définir un deuxième constructeur qui prend en paramètre le point correspondant au coin inférieur gauche, puis la longueur et la largeur
  4. Ajouter une méthode qui teste si un point est contenu dans le rectangle courant
  5. Ajouter la méthode qui retourne le périmètre
  6. Puis celle qui retourne l'aire
  7. Ajouter une méthode qui teste si un rectangle est contenu dans le rectangle courant
  8. Ajouter une méthode toString

#6.5. Polygone

  1. Ecrire une classe Polygone où un polygone est un tableau de points. Elle aura un constructeur prenant en paramètre un tableau de points.
  2. Ajouter une méthode permettant de calculer le nombre de sommets. Pour obtenir la longueur d’un tableau on lui applique la méthode length.
  3. Ajouter une méthode permettant de calculer le périmètre.
  4. Ecrire une méthode toString pour afficher la liste des sommets d’un polygone
  5. Ajouter une méthode toString

#6.6. Points 3D

Quelle solution proposez vous pour la classe des Points 3D ? Implémentez la.


#7. Eléments de langage Java (compléments du cours)

#7.1. Convention d'écriture

Mots clés : en minuscules

  abstract boolean  break  byte case catch char  class  const  continue 
  default  do  double  else  extends final finally float  for  goto 
  if  implements import instanceof int interface  long  native new  package
  private  protected  public  return  short static  super switch  synchronised this 
  throw throws transient try void volatile  while

Constantes : séparées par des _ et CAPITALISÉES

  MA_CONSTANTE

Variables et méthodes : en minuscules, si composées de plusieurs mots, chaque première lettre est CAPITALISÉE à l'exception de celle du premier mot

  maVariable

Classes : en minuscules, la première de chaque mot est CAPITALISÉE

  MaClasse, System

#7.2. Packages

#7.2.1. Principe

Un package est un regroupement cohérent et thématique de plusieurs classes. Au niveau du code source, un package n'a pas d'existence explicite, il n'est réellement créé que dès que sa première classe est compilée.

L'API Java est elle-même structurée en packages organisés de manière hiérarchique (peuvent contenir des sous-paquetages)

Les principaux packages sont :

Documentation complète : http://docs.oracle.com/javase/8/docs/api/

#7.2.2. Déclaration

Pour indiquer qu'une classe appartient à un package on utilise au tout début du fichier source le mot-clé package. Si pas d’instruction, le package est default

package monpackage;
public class MaClasse {
   ...
}

Ex. classe Integer du package lang

package lang;
public class Integer extends Number { 
  ... 
}

Rq : Les packages peuvent être imbriqués les uns dans les autres.

  package fr.ensiie.fipa.ipro;

#7.2.3. Utilisation

Quand une classe fait référence à une autre classe d‘un package différent, elle doit l’importer avec le mot-clé import

package autrepackage;

import monpackage.MaClasse;

public class AutreClasse {
   MaClasse[] c;
   ...
}

Il est également possible d'importer toutes les classes d'un package en utilisant le wildcard * mais ce n'est pas recommandé

import bibliotheque.*;
Exemples

Définition d'une fenêtre

import java.awt.* ;
import javax.swing.* ;  // contient la classe JFrame.
public class MaFenetre extends JFrame { ... }

Classe quelconque

import java.lang.Object ; // facultatif car java.lang est importé par défaut
public class Bidule {
  public Object clone() {
    return new Bidule(); 
  }
}

#7.2.4. Compilation (console)

A la compilation, des répertoires correspondants aux packages définis sont créés pour contenir les bytecodes Une classe est alors désignée par son nom de package, suivie d'un point puis du nom de la classe.

java monpackage.MaClasse

Pour que la référence à la classe MaClasse soit effective, il faut que le répertoire contenant son package soit accessible par la machine virtuelle. Il faut alors préciser le chemin d'accès du répertoire contenant le package par l'option -classpath comme précédemment :

java -classpath /dossierbytecode monpackage.MaClasse

#7.3. Types

#7.3.1. Types primitifs

Ne sont pas considérés comme des objets en Java :

#7.3.2. Forcer un type (cast)

Java permet des conversion implicites entre types primitifs : byte|char$\rightarrow$short$\rightarrow$int$\rightarrow$long$\rightarrow$float$\rightarrow$double

Le cast (ou transtypage) permet de forcer le programme à considérer une expression comme étant d'un type qui n'est pas son type réel ou déclaré : (type_souhaité) expression

Ex. division non entière

  int x = 10, y = 3; 
  double z = (double)x / y; // pour avoir 3.3333.. et pas 3.0, cast de x suffit

Limites : on peut uniquement caster

#7.3.3. Classes enveloppes

Certaines manipulations nécessitent de travailler avec des objets plutôt que des valeurs de types primitifs

java.lang fournit

NB : les instances de ces classes ne sont pas modifiables (comme String)

#7.3.4. Type énuméré

Permet de définir un nouveau type en énumérant toutes ses valeurs possibles

Plus sûr que d'utiliser des entiers pour coder les différentes valeurs du type (vérifications à la compilation)

enum type { VALEUR1, VALEUR2 ...};

Rq : majuscules par convention

enum dans une classe
public class Carte {
    public enum Couleur {TREFLE, CARREAU, COEUR, PIQUE};
    private Couleur couleur;
    ...
    this.couleur = Couleur.PIQUE;
}
carte.setCouleur(Carte.Couleur.TREFLE);
enum sans classe
//dans fichier CouleurCarte.java
public enum CouleurCarte {
    TREFLE, CARREAU, COEUR, PIQUE;
}
//dans Carte.java
public class Carte {
    private CouleurCarte couleur;
    ...
    couleur = CouleurCarte.PIQUE;
}

Rq : peut comporter des méthodes, des constructeurs, implémenter des interfaces, on ne peut pas en hériter

Enumérations : méthodes

#7.3.5. Chaînes de caractères String

java.lang.String : chaînes constantes

String chaine1 = "Bonjour";
String chaine2 = new String("Hello");
chaine1 = "Hi";     // nouvelle valeur créée, référencée par chaine
                    // ancienne chaine pourra être libérée

Concaténation avec +

int x = 5;
String s = "Valeur de x = " + x;

Traduction auto des types primitifs par le compilateur

Traduction des objets avec la méthode toString() de la classe

Test d'égalité de contenu avec equals()

Voir autres méthodes dans la doc

#7.3.6. Chaînes de caractères StringBuilder et StringBuffer

Chaînes modifiables : possèdent des méthodes qui modifient le receveur du message et évitent la création de nouvelles instances

Conversions

String[] t;
// Concaténation des éléments de t dans chaine
StringBuilder sb = new StringBuilder(t[0]);
for (int i = 1; i < t.length; i++)
    sb.append(t[i]);
String chaine = c.toString();

#7.4. Tableaux

#7.4.1. Déclaration

int[] monTableau;
double[] listeReels;
long[][] tableau2D;

#7.4.2. Création

monTableau = new int[12];
listeReels = new double[23];
tableau2D = new long[5][4];

#7.4.3. Utilisation

for (int i = 0 ; i < monTableau.length ; i++)
    monTableau[i] = 2 * i; 

#7.4.4. Tableaux à plusieurs dimensions

Les considérer comme des tableaux de tableaux

int t[][] = new int[4][];
for (int i = 0 ; i < t.length ; i++)
{ 
    t[i] = new int[4 - i]; 
    for (int j = 0 ; j < t[i].length ; j++) 
    {
        t[i][j] = i + j;
    }
} 

#7.5. Introspection

Consiste à obtenir des informations sur les objets

#7.5.1. public Class getClass()

Renvoie la classe de l'objet du type java.lang.Class

Une instance de la classe Class représente un type (classe, interface, type primitif, tableau, énumération) utilisé par l'application

#7.5.2. Tests d'appartenance à une classe

Opérateur instanceof

objet instanceof NomClasse

=> Tester l'égalité entre classes

if (o.getClass() == this.getClass()) ...

#7.6. Classe Object et méthodes classiques

java.lang.Object est la racine de l'arbre d'héritage des classes

#7.6.1. public String toString()

Renvoie une description de l'objet sous la forme d'une chaîne de caractères

A redéfinir pour afficher les infos utiles dans les nouvelles classes (ex. valeurs des attributs)

public class Livre {
    ...
    public String toString() {
      return "Livre [titre=" + titre + ", auteur=" + auteur + 
      ", nbPages=" + nbPages + "]";
    }
}

Rq : System.out.println(o) et la concaténation des String font appel implicitement à o.toString()

#7.6.2. public boolean equals(Object obj)

Renvoie true si et seulement si l'objet courant this a “la même valeur” que l'objet obj

La méthode equals de Object renvoie true si this référence le même objet que obj (identique à ==)

A redéfinir dans les classes pour lesquelles on veut une relation d'égalité différente (ex. comparaison structurelle ou “profonde” liée aux valeurs des attributs)

#7.6.3. equals : règles

#7.6.4. equals : algorithme classique

@Override
public boolean equals(Object o) {
    if (o == this) return true; 

    if (o == null) return false;

    if (!(o instanceof MaClasse)) return false;
    
    else {
      // caster 
      MaClasse c = (MaClasse) o;
      // comparer les attributs
      ...
    }
}

#7.6.5. public int hashCode()

Fournit une fonction de hachage par les tables de hachage du JDK

Pas de formule universelle : la valeur calculée doit

=> Toute classe qui redéfinit equals() doit donc redéfinir hashCode()

#7.7. Javadoc

javadoc est un autre programme du SDK qui génère une documentation automatique à partir du code source de classes Java. Cette documentation automatique décrit les membres d'une classe dans un format HTML. Pour créer tous les fichiers de documentation d'un package, on utilise javadoc comme suit:

javadoc -d repertoireDestination nomdespackages

Le fichier principal de documentation est index.html créé dans le répertoire de destination.

Remarque : par défaut la commande javadoc ne génère la documentation que pour les classes et membres public, donc pensez à établir la visibilité de votre classe à public. Vous pouvez également spécifier à javadoc que vous voulez également générer la documentation pour les membres privés avec l'option -private :

javadoc peut interpréter des commentaires spécifiques introduits dans le code source pour enrichir la documentation générée. Ces commentaires se situent juste avant la déclaration d'une classe, d'un attribut ou d'une méthode. Ils commencent par /** et se terminent par */. Ces commentaires contiennent une partie textuelle libre et des tags interprétés pour certains commentaires spécifiques.

Sous Eclipse, on peut passer par le menu Projet -> Generate Javadoc…

Exemple de classe commentée pour javadoc
package bibliotheque;
/**
 * Cette classe est utilisée pour représenter un livre.
 *
 * @author Laurent Vercouter
 */
public class Livre extends Produit {

   /**
   * Le titre du livre
   */
   private String titre;

   /**
   * L'auteur du livre
   */
   private String auteur;

   /**
   * Le constructeur de la classe Livre
   *
   * @param tit Le titre du livre
   * @param aut L'auteur du livre
   */
   public Livre(String tit, String aut) {
       titre = tit;
       auteur = aut;
   }

   /**
   * Cette méthode renvoie une chaîne de caractères qui décrit
   * textuellement le livre (par son titre, son auteur et l'éditeur)
   *
   * @return Une chaîne de caractère décrivant le livre
   */
   public String description() {
       return "\""+titre+"\" de "+auteur+" edite par "+editeur;
   }
}