Outils pour utilisateurs

Outils du site


developpement:java:introduction

Introduction au langage Java

Java est à la fois un langage de programmation informatique orienté objet et un environnement d'exécution informatique portable créé par James Gosling et Patrick Naughton employés de Sun Microsystems avec le soutien de Bill Joy (cofondateur de Sun Microsystems en 1982), présenté officiellement le 23 mai 1995 au SunWorld1).

Versions de Java

Le JSDK est le principal produit de Sun conscernant Java. Il est téléchargeable gratuitement2).

Le JSDK comprend

  • Bibliothèque de base
  • Compilateur : javac
  • Interpreteur

Installation

  1. Lancer le package téléchargé
  2. Vérifier la présence du chemin vers le répertoire créé dans le PATH du sstème
  3. Une vrariable d'environnement CLASSPATH indique la liste des répertoires contenant des bibliothèques Java
  4. Une variable d'environnement JAVA_HOME indique le répertoire dans lequel Java est installé

De l'édition à l'exécution

Quand on a terminé d'éditer un programme Java (MyProgram.java), on le passe au compilateur qui le transforme en ByteCode (MyProgram.class). Une fois le fichier « compilé » en ByteCode, ce dernier doit être interprété par une marchine virtuelle, disponible sous différentes plateformes.

(graphique)

Applets

En java on peut écrire deux types de programmes

  • Application
  • Applet

Éléments du langage

  • Java est sensible à la casse
  • Le programme est une classe
  • Import est équivalent du include en C/C++
  • Comme en C/C++, lafonction (méthode) principale est main()
  • Les classes sont regroupées en packages (paquetages, paquets)
  • Convention des choix d'identificateurs
    1. Le nom de classe commence par une majuscule
    2. Le nom de méthode ou attribut commencent pas une majuscule
    3. Le deuxième mot et suivants commencent par une majuscule
    4. Exemples :
      • Classes : Point, Employé, Bouton, CompteEpargne
      • Attributs et méthodes : salaire, premierEmployeur, dateDeNaissance, calculPaie(), getSurface(), printDateDeNaissance

Commentaires

// ====== CONSTRUCTEURS ========================================================== //
// ====== GETTERS ================================================================ //
// ====== SETTERS ================================================================ //
// ====== MÉTHODES =============================================================== //

Il est conseillé de documenter son code avec javadoc.

Exemples de programmes

//Fichier : Bienvenue.java
 
/**Ceci est un commentaire javadoc.*/
 
public class Bienvenue {
 
  /*
  Comme en C/C++, la fonction main() est le point d’entrée de l'application. 
  */
 
  public static void main(String s[]) {
    System.out.println("Bonjour et Bienvenue");
  }
}
//Fichier : Montant.java
public class Montant {
  public static void main(String s[]) {
    final double TPS = 0.07;
    final double TVQ = 0.075;
 
    double prixUnit=10;	//le prix unitaire.
    int quantite=10;
    double mHt, mTps, mTvq, mTot;
 
    mHt = prixUnit * quantite;
    mTps = mHt * TPS; mTvq = (mHt + mTps)*TVQ;
    mTot = mHt + mTps + mTvq;
 
    System.out.println("Montant hors taxes : "+mHt+
        "\nMontant TPS : "+mTps+"\nMontant TVQ : "+mTvq+
        "\nMontant total : "+mTot);
  }
}

Types de données

Il y a deux grandes catégories de types de données en Java

  • Primitifs (simples) → Donnée de base du langage
  • Composites (objets) → Donnée encpsulée dans une classe

Les types de données primitifs sont indiqués tout en minuscules et les types de données composites sont nommés avec la première lettre en majuscules (tout comme la convention pour nommer les classes).

Types de données primitifs

Type Catégorie Espace mémoire Précision
byte entier 1 octet -128 → 127
short entier 2 octets -32768 → 32767
int entier 4 octets 2 milliards
long entier 8 octets -264 → 264-1
float réel 4 octets 3.40282347E+38
double réel 8 octets 1.79769313486231570E+308
char caractère 2 octets 65535 codes (unicode)

Types de données composites

Classe Catégorie
Integer Entier
Long Entier
Float Réel
Double Réel
Caracter Caratère
Boolean Booléen

Déclarations

Quelques exemples pour déclarer des variables ou des objets.

Syntaxe générale :

  type nomVariable;

Délaration - Initialisation :

  • Variable de type simple → type nomVariable = valeur;
  • Variable de type objet → NomClasse nomVariable=new NomClasse(…);;

Les variables (non locales à une méthode) sont automatiquement initialisées (0 pour les numériques et caractère, null pour les objets, false pour les booléens).

Opérateurs et expressions

Liste des opérateurs par priorité décroissante :

# Opérateur
1 [] . ()
2 ! ~ ++ – + - (cast) new
3 * / %
4 + -
5 << >> >>>
6 < <= > >= instanceof
7 == !=
8 &
9 ^
10 |
11 &&
12 ||
13 ?:
14 = += -= *= /= %= &= |= ^= <<= >>= >>>=

Assocativité :

  1. Les opérateurs unaires (2) et d'affectation (14) → de droite à gauche
  2. Les autres → de gauche à droite
  3. Pour forcer la priorité utiliser les paranthèses

Opérateur ternaire

Aussi appelé ternary operator.

boolean expression ? valeurSiVrai : valeurSiFaux

Exemple :

String humeur = estDeBonneHumeur ? "Je suis content" : "Je ne suis pas content";

Structures et contrôles

Séquence

  • Toute instruction se termine par un point-virgule (;)
  • Un bloc de plus d'une instruction est délimitée par des accolades ({,})

Conditionnelles

switch (variable) {
  case valeur1 :
	Liste d'instructions
	break;
 
  case valeur2 :
	Liste d'instructions
	break;
 
  case valeurn :
	Liste d'instructions
	break;
  default: 
	Liste d'instructions
	break;
}

Boucles

foreach

Le code suivant fait une sorte de foreach en Java. Attention, dès que ce code trouve un élément avec isSelected à true, il le supprime et sort de la boucle for.

for (ColoredBox coloredBox: scene.coloredBoxes)	
  if (coloredBox.isSelected)
    scene.coloredBoxes.removeElement(coloredBox);

Structures de données

Tableaux

  int[] t1;      // tableau d'entiers
  int t2[];      //   "         "
  int[] x, y;    // x et y sont des tableaux
  int x, y[];    // seleuement y est un tableau
  Point[] t3;    // tableau de Point (objets)
  t1 = new int[20];
  t3 = new Point[10];

Programmation Orientée objet

La programmation orienté objet comprend plusieurs concepts :

  1. Classes et objets
  2. Encapsulation
  3. Héritage et polymorphisme
  4. Interfaces
  5. Modes d'accès (private, public, protected)

Structure d'une classe

Le nom de la classe et le nom de fichier sont liés, c'est-à-dire qu'une classe Etudiant doit avoir comme nom de fichier Etudiant.java.

Structure générale d'une classe

<nom de la classe>
<attributs> Décrivent l'état (habituellement private)
<méthodes> Constructeurs Instancier l'objet
<méthodes> Décrivent le comportement (public)
public class Etudiant {
  private String nom, prenom, adresse;
  private int age;
 
  // Constructeurs
  public Etudiant(String n, String name, String pname) {
    nas = n; nom=name; prenom=pname;
    age=16; adresse="Montréal";
  }
 
  public void demenage (String nouvelleAdr) {
    adresse = nouvelleAdr;
  }
}

Dans un programme Java :

  Etudiant e1 = new Etudiant ("123123123", "Pierre", "Jean");
  Etudiant e2;
  e2 = e1;

Les variables e1 et e2 sont des références (synonymes à pointeur) qui sont liés à un objet. Quand on fait e2 = e1;, l'adresse mémoire de l'objet e1 est copiée dans la variable e2.

Classe interne

public Class ClasseHote {
 
  [...]
 
  private class ClasseInterne {
 
    [...]
 
  }
}

Méthodes

Méthodes d'accès :

  • en lecture (getter) → Utilisé pour lire la valeur d'un attribut : comme getValeur ira chercher la valeur de l'attribut valeur.
  • en écriture (setter) → Utilisé pour déterminer la valeur d'un attribut : comme setValeur donnera une nouvelle valeur à l'attribut valeur.

Surcharge et surdéfinition

La surcharge (overload) d'une méthode c'est d'implémenter une nouvelle méthode ayant le même nom et étant dans la même classe, mais avec une signature différente, ou plutôt avec des paramètres différents.

La surdéfinition (overriding) c'est d'implémenter une méthode différemment qui a été héritée par une classe parent.

Référence this

Le this fait référence à l'objet lui-même. On peut utiliser la référence this dans une méthode quand un identificateur fourni en paramètre a le même nom qu'un attribut :

public class Personne {
 
  private String nom;
 
  public void setNom (String nom) {
    this.nom = nom;
  }
}

On peut aussi utiliser this pour surcharger le constructeur le l'objet :

public class Point {
 
  private double abscisse, ordonnee;
 
 
  public Point(double a, double b) {
    abscisse=a;
    ordonnee=b;
  }
 
  public Point(double a) {
    this(a,0);
  }
 
  public Point() {
    this(0);
  }
 
}

Il est plus simple par la suite de modifier le code pour ajouter un attribut :

public class Point {
 
  private double abscisse, ordonnee;
 
 
  public Point(double a, double b) {
    this(a,b,0);
  }
 
  public Point(double a) {
    this(a,0);
  }
 
  public Point() {
    this(0);
  }
 
  public Point (double a, double b, double c) {
    abscisse = a;
    ordonnee = b;
    profondeur = c;
  }
 
}

Imbrication d'objets - Aggrégation / Composition

La classe Adresse :

public class Adresse {
  private String numRue,
         nomRue,
         codePostal,
         ville;
 
  public Adresse (String n, String r, String c, String v) {
    numRue = n;
    nomRue = r;
    codePostal = c;
    ville = v;
 
  }
 
  public void setVille (String v) {
    ville = v;
  }
 
  public String getVille () {
    return ville;
  }

La classe Personne :

public class Personne {
  private String nas, nom, prenom;
 
  public Adresse adresse; 
 
  public Adresse getAdresse () {
    return adresse;
  }
}
Personne a = new Personne();
// afficher à l'écran la ville ou habite la personne a
Adresse adr = a.getAdresse();
String v= adr.getVille();
System.out.println(v);
 
// ou bien :
System.out.println(a.getAdresse().getVille());

Transmission et retour d'objets par des méthodes

Les données primitives sont toujours transmises par valeur.

public void incrementer (int x) {
  x++;
}
// ...
int a=8;
incrementer(a);
System.out.println("a="+a); // affiche : a=8

Les objets sont toujours transmis par référence. Les références sont transmises par valeur.

public class Point {
  private double abscisse, ordonnee;
  public void setAbs(double a) {
    abscisse = a;
  }
}
 
public void changerAbs (Point x, double abs) {
  x.setAbs(abs);
}
 
public void permute (Point x, Point y) {
  Point tmp=x;
  x=y;
  y=tmp;
}
Point p = new Point();
chagerabs(p,35);
//l'abscisse de p vaut 35

Quand on

Point p = new Point(), q = new Point();
permute(p,q);

Packages

Les packages en Java sont des regroupement de classes.

Exemples de packages :

  1. javax.swing → programmation graphique
  2. java.sql → programmation sur bases de données
  3. java.net → programmation réseau

Avec javax.swing.JFrame, le fichier de classe (JFrame.class) se trouve dans un dossier javax que celui-ci contient un sous-dossier swing. Cette structure ne se trouve pas nécessairement sur le disque-dur, il peut se trouver dans un fichier JAR.

L'emplacement des packages n'est pas important, l'important c'est de vérifier la variable d'environnement CLASSPATH.

L'importation de classes/packages

import javax.swing.*;
 
public class Programme {
  public static void main(String s[]) {
    JFrame fen = new JFrame ("Titre");
  }
}

Ajouter une classe à un package

package com.sgariepy.geometrie;  // toujours la premiere ligne du code
 
// éventuels import

public class Cercle {
 
}

La convention dans le choix de noms packages est d'utiliser le nom de domaine inversé. Par exemple ibm.com nommera ses packages com.ibm.*.

Spécificateurs d'accès

La visibilité, du moins visible au plus visible :

Nom Notation UML
Privé private -
Protégé protected #
Paquetage package ~
Public public +

L'accès par défaut (accès friendly ou accès package).

Avec la déclaration

Package 1 :

public class Classe1 {
  private int a;
  int b;
 
  Class2 x;  // ok
 
}
 
class Class2 {
  Class1 u = new Class1();
  u.b = 36;  //ok
}

Package 2 :

public class Classe3 {
  Class2 y; // non, car Class2 qui a un acces "protegé" n'est pas visible de l'exterieur de son package
 
}

Ayant une classe mère Personne et une classe fille Étudiant. Dans Personne si l'attribut NAS est private la classe fille n'a pas accès. En ayant un attribut nom qui est protected, la classe Étudiant aura accès. Si aucun spécificateur d'accès n'est spécifié dans la classe mère pour un attribut prenom, alors cet attribut est accessible seulement si les deux classes sont du même package.

Modificateurs

  1. abstract → classe ou méthode abstraite
  2. static → méthode ou attribut de classe
  3. final
    1. l'attribut ou variable constante
    2. peut s'utiliser aussi avec une méthode, qui ne pourra être surdéfinie
    3. classe non dérivable

Attributs et méthodes de classes (static)

public class Groupe {
  private Etudiant[] etudiants;
  private int nbEtudiants;
  private static int capaciteMaximale = 20; // Note1
 
  public static int getCapaciteMaximale () {    // Note2
    return capaciteMaximale;
  }
 
}

Note1 → L'attribut capaciteMaximale restera pour tous les instances de la classe. Une modification se fait partout.

Note2 → Pour accéder à l'attribut static sans instance de classe, il faut que la méthode soit elle-même static. Les méthodes static ne peuvent pas toucher aux attributs non static.

static void main(...) {
  Groupe g1 = new Groupe(),
         g2 = new Groupe(),
         g3 = new Groupe();
}
public class Taxes {
  public static final double TPS = 0.05;  // Note3
  public static final double TVQ = 0.075;
}
  double montantHT = 300;
  double montantTPS;
  montantTPS = montantHT * Taxes.TPS;

Note3 → Comme les attributs de la classe Taxes sont finaux (final), il est possible, sans problème de les mettre public. Ensuite, dans un programme, on pourra accéder directement à l'attribut : Taxes.TPS.

Autre exemple : la classe Math → Math.PI.

  1. Math.random(); → nombre aléatoire (0 ⇐ x < 1)
  2. Math.pow(x,y); → xy
  3. Math.sin(angle);

Chaînes de caractères

Déclarer un tableau de chaînes

String sTab[] = new String[5];

Comparer deux chaînes

String chaine1 = "abcd";
String chaine2 = "xyz";
 
if (chaine1.compareTo(chaine2)==0) {
    // logique
}
Résultat Signification
<0 La chaine se trouve avant, lexicographiquement
== 0 Les deux chaînes sont équivalentes, lexicographiquement
>0 La chaine se trouve après, lexicographiquement

StringBuffer

public class Personne {
  private String nas, nom;
  private int age;
 
  public int getAge {
    return age;
  }
  public String getNAS {
    return NAS;
  }
 
}
static public void main () {
  Personne = new Personne ();
 
  int a p.getAge();
  a -= 10;           // l'age de la persone nest pas modifiee
 
  String s = p.getNAS();  // cette fonction retourne une référence vers la chaine du NAS de la personne, mais la string est inchangeable
}

Pour travailler sur des chaînes de caractère altérables, il faut utiliser StringBuffer.

Tableaux

Principes

  • Les tableaux sont des objets particuliers.
  • Si on affecte un tableau à un autre, on fait en réalité une affectation de références.
  • Chaque tableau a un attribut public lengthqui représente la taille du tableau.
  • On peut spécifier la taille d'un tableau à l'exécution mais cette taille ne peut plus être changée.
  • Si on tente d'accéder à un élément en dehors des limites déclarées, Java lance une exception de type IndexOutOfBoundsException.
  • Le premier indice du tableau est toujours 0.

Déclaration, instanciation et initialisation

Déclaration

int[] T; //ou : int T[]; 
Point[] TP; //ou : Point TP[];

Instanciation

T = new int[5]; 
TP = new Point[3];

Attention! Les objets contenus dans TP ne sont pas encore créés:

TP[1].setAbs(25); //NON 
T[1] = 26 //OK 

Initialisation

int[] T = {8,6,23,51,49}; 
Point[] TP = {new Point(11,3),new Point(7,2),new 
Point(4,9)}; 

Affectation et copie

Les tableaux étant des objets, l'affectation copie les références.

Pour la copie on utilise la méthode arrayCopyde la classe System :

System.arrayCopy(T1,0,T2,0,3); 
System.arrayCopy(T1,debutsource,T2,debutdest,nbElements);

Arguments de méthodes

  • Une méthode peut recevoir un tableau en argument au même titre que n'importe quel objet.
  • La transmissionse fait toujours par référence
  • Un méthode peut retourner un tableau comme résultat au même titre que n'importe quel objet.
  • Le retour se fait toujours par référence.
  • La classe java.util.Arrays contient plusieurs méthodes pour la gestion de tableaux(copie, recherche binaire, test d'égalité,…).
  • La classe java.util.Arrays contient la méthode sort() qui trie un tableau en utilisant l'algorithme quicksort.

Tableaux multidimentionels

  • Ce sont en réalité des tableaux de tableaux (comme en C/C++) :
double mat[][] = new double[4][3];//matrice 4x3. 
  • On peut initialiser mat comme ceci :
double mat[][] = {{3,6,2},{7,1,5},{11,9,54},{1,5,6}}; 
  • mat est un tableau de taille 4 dont chaque élément est un tableau de 3 doubles.
  • Cependant, contrairement à C/C++, chaque ligne étant un objet, on peut permuter 2 lignes en écrivant simplement :
double tem[] = mat[i]; 
mat[i] = mat[j]; 
mat[j] = temp; 
  • On peut créer des tableaux avec des lignes de différentes tailles :
double M[][] = new double[4][]; 
for (i=0;i<M.length;i++) 
  M[i] = new double[2*i+1]; 

Les vecteurs

Un vecteur (java.util.Vector) est un objet qui encapsule un tableau de taille variable. Ce tableau ne peut contenir que des objets. <html><br/></html> Avant JDK 1.5

Vector v = new Vector();

Dès la création du vecteur, une place pour le tableau est réservé en mémoire.

Point p = new Point(5,2);
v.add(p);
Integer x = new Integer(48);
v.add(x);
String nom = "Bernard";
v.add(nom);

La méthode addElement() fait le même travail que add().

Contenu du vecteur v Index
p de type Point 0
x de type Integer 1
nom de type String 2
3
4
5
  if (v.contains(q));  //q et étant deux Point ayant le meme abscisse/ordonnee, cela retourne vrai
  Object o = v.get(2);
  Object m = v.get(0);
  int a1 = p.getAbscisse();  //OK
  int a2 = m.getAbscisse();  // NON
  Point n = (Point) v.get(0);
  int a3 = m.getAbscisse(); //OK

<html><br/></html> À partir de JDK 1.5

  Vector<Point> vp = new Vector<Point>();
  Point p = ...;
  vp.add(p);
  Point r = vp.get(0);  // inutile de faire un transtypage

<html><br/></html> La méthode toArray()

Cette méthode permet de retourner un tableau de références des objets.

  Object[] = v.toArray();
  ((Point) t[0]).setAbscisse(10); //premiere paranthèses pour forcer la priorité sur le cast

Héritage

Exemple d'une personne. Une personne a plusieurs attributs (nas, nom, prenom, age, adresse) et méthodes (changerAdresse(String nouvelleAdr)). Un Étudiant est une (relation is a) Personne. Si un Étudiant n'a pas de NAS, il y a problème de conception, c'est-à-dire qu'elle n'est pas vraiment une personne.

Apports de l'héritage :

  • Réutilisation du code
  • Faciite la maintenance du code
  • Permet des traitements génériques

L'instance Étudiant qui dérive de Personne, n'a pas accès aux attributs de Personne : NAS, nom, prenom, etc. C'est pourquoi on utilise un spécificateur d'accès moins rigide que private : protected.

public class Etudiant extends Personne

 Diagramme de classe (Étudiant est une Personne)

public class Personne {
  protected String NAS, Nom, Prenom, Adresse;
 
  public void changerAdresse(string nouvAdr) {
 
    //...
  }
 
  public void print () {
    System.Out.print ("NAS:"+NAS+
      "\nNom: "+nom+
      "\nPrenom:"+prenom);
  }
}
 
public class Etudiant extends Personne {
 
  public void print () {
    super.print();       // apelle la méthode print de la classe mère
    System.Out.print ("NAS:"+NAS+
      "\nNom: "+nom+
      "\nPrenom:"+prenom);
  }
 
  public String getSession () {
    return session;
  }
 
}

Référencement entre classes parent et filles

Une référence peut pointer sur différents types d'objets, selon s'ils sont hérités ou non. Conceptuellement, une Personne est une Personne, un Etudiant est un Etudiant et un Etudiant est une Personne. Par contre, une Personne n'est pas un Etudiant. Voir l'exemple suivant :

Personne p1, p2;
Etudiant e1, e2;
 
p1 = new Personne();  //OK
e1 = new Etudiant();  //OK
p2 = new Etudiant();  //OK
e2 = new Personne();  //NON

Liaison dynamique

Ce qui est intéressant, c'est si nous avons un tableau de références de type Personne, mais qui pointent vers différents types de personnes (Étudiants, Employés, Militaires, etc), la méthode print correspondant au type spécifique de l'objet (fils) est appelé.

String s1 = p1.getSession(); //NON
String s2 = p2.getSession(); //NON - getSession n'est pas définie dans la classe mère (p2 est une référence Personne) - il faut un Cast
  String s2 = ((Etudiant) p2).getSession(); //OK - s'assurer que p2 fait bien référence à un étudiant (p2 instanceof Etudiant)
String s3 = e1.getSession(); //OK
p1.print();  //OK, appel à print de personne
p2.print();  //OK, appel de print de Etudiant (liaison dynamique)
e1.print();  //OK, appel de print de Etudiant

Méthodes finales

On peut spécifier une méthode comme étant final. Cela ura pour effet qu'une classe fille dérivée de la classe mère ne pourra pas redéfinir cette méthode. Par exemple, la classe Personne contient une méthode getNAS(), on peut la mettre finale puisque sa seule et unique fonction est de retourner le NAS.

import java.util.*; 
 
public class Personne {
 
  public final String getNAS() {
    return nas;
  }
}
 
public class Etudiant extends Personne {
  public String getNAS() {   //NON : getNAS() est finale.
    return nas;
  }
}

Dans l'API, la classe classe String est finale.

Méthodes héritées

Certaines méthodes sont héritées de classes parent et il peut être intéressant de les redéfinir.

Par exemple la méthode equals :

public boolean equals (Object p) {
  if (p instanceof Point) {
    Point t = (Point) p;
    return (x == t.getX() && y == t.getY());
  }
  else return false;
}

Classes inernes

  • C’est un luxe plutot qu’une nécessité.
  • C’est une classe définie à l’intérieur d’une autre classe.
  • Permet d’accéder directement aux données de la classe qui la contient.
  • Une classe interne est cachée aux autres classes.
  • Très utilisées dans les interfaces utilisateurs et dans la gestion des événements.
public class X {
 
  class Y {
 
  }
 
}

Une classe interne (Y) a accès aux attributs privés de la classe « externe » (X). De cette façon, la classe X n'a pas accès aux attribut de la classe interne Y. On peut aussi spécifier des attributs public à la classe Y et spécifier cette même classe comme étant private. De cette façon, la classe X a accès aux données de la classe interne.

Exemple :

class Personne {
  ...
  public JPanel getUI() {
    return new PersonnePanel();
  }
 
  class PersonnePanel extends JPanel {
    //création de l’interface graphique d’une
    //personne sous forme d’un panneau.
  }
}

Puis, dans un programme :

  JFrame fenetre x = new JFrame();
  Personne x = new Personne();
  JPanel xUI = x.getUI();
  fenetre.getContentPane().add(xUI);

Classes abstraites

  • Sert de base à d’autres classes, qui vont en dériver.
  • Impose un format commun aux classes dérivées.
  • Améliore la clarté et facilite la conception des classes.
  • Ne peut pas être instanciée.
  • Contient une ou plusieurs méthodes abstraites.
  • Les méthodes abstraites doivent être surdéfinies par toutes les classes non abstraites dérivées.
  • Si une classe contient une méthode abstraite alors elle est abstraite même si elle n’est pas déclarée comme telle.

Exemple de classe abstraite :

public abstract class Figure {		//Fichier:Figure.java
  public boolean getVisibilite() {
    return visible;
  }
 
  public void setVisibilite(boolean v) {
    visible = v;
  }
 
  public abstract double getSurface();
 
  private boolean visible;
}

Dans le cas suivant, on définit une classe qui hérite de la classe Figure (abstraite). On implémente la méthode getSurface().

public class Cercle extends Figure {	//Fichier : Cercle.java
  public Cercle() {
    centre = new Point(0,0);
    rayon = 10;
  }
 
  public Cercle(double r) {
    centre = new Point(0,0);
    rayon = r;
  }
 
  public Cercle(Point c, double r) {
    centre = c;
    rayon = r;
  }
 
  public double getSurface() {
    return Math.PI*Math.pow(rayon,2);
  }
 
  private Point centre;
  private double rayon;
}

La classe suivante est celle d'un Rectangle. On implémente aussi la méthode getSurface() de façon différente de Cercle.getSurface().

public class Rectangle extends Figure  {    //Fichier : Rectangle.java
  // ...
 
  public Rectangle(double L, double l) {
    centre=new Point(0,0);
    longueur=L;largeur=l;
  }
 
  public double getSurface() {
    return longueur*largeur;
  }
 
  public double getLongueur() {
    return longueur;
  }
 
  private Point sommetHG;
  private double longueur, largeur;
}

Exemple de code avec ce qui est possible et impossible de faire :

class TestFigure {	//Figure : TestFigure.java
 
  public static void main(String args[]) {
    Figure f1, f2, f3;
    f1 = new Figure(); //NON. Figure est abstraite
    f2 = new Cercle(); //OK
    System.out.println(f2.getSurface());
    f3 = new Rectangle(9,3);
    System.out.println(f3.getSurface()); //OK
    System.out.println(f3.getLongueur());//NON
    System.out.println((Rectangle)f3.getLongueur());//NON
    System.out.println(((Rectangle)f3).getLongueur());//OK
  }
}

Interfaces

L'interface est une classe abstraite encore plus généralisée. Elle sert

  • L’héritage multiple rend les compilateurs soit complexes (C++) soit peu performants (Eiffel).
  • Le concept d’interface a été introduit par java pour récupérer une bonne partie des fonctionnalités de l’héritage multiple : amener une classe à reflèter le comportement de plusieurs classes parentes.
  • Une interface est une classe abstraite déclarée avec le mot reservé interface au lieu de class.
  • Une interface ne peut contenir que des méthodes abstraites et des constantes.
  • Une interface ne peut pas avoir des attributs.
  • Une classe peut implémenter une interface : elle doit alors définir toutes les méthodes de l’interface.
  • Une classe ne peut dériver que d’une seule classe mais elle peut implémenter plusieurs interfaces.

Création d'une interface :

interface MonInterface {
  public void f1(...);
  public double f2(...);
}

Implémentation de l'interface :

public class ABC implements MonInterface {
  ... 
 
  public void f1(...) {
    ... 
  }
 
  public double f2(...) {
    ... 
  }
}

Propriétés d'une interface :

  • On peut déclarer des objets d’une interface :
  MonInterface x1, x2;
  x1 = new ABC();
  • On peut utiliser l’opérateur instanceof pour vérifier si un objet implémente une interface :
if (x1 instanceof MonInterface) ... 
  • On peut faire de l’héritage d’interfaces :
public interface MonInterface2 extends MonInterface {
  public void f3(...);
}
public class Util {
  public void trier (Cercle t[]) {
    for (int i = 0; i<t.lentgh-1; i++)
      for (int j=i+1; j<t.lentgh; j++)
        if (t[i].getSurface() > t[j].getSurface[]) {
          Cercle temp;
          temp = t[i];
          t[i] = t[j];
          t[j] = temp	
        } 
  }
}
 
 
// ...
 
main () {
	Cercle t[] = new Cercle t[50];
	// intanciation des 50 cercles
	// ...
	Util.trier(t);
}

Comparable

Pour réellement comparer des objets entre eux, plus qu'avec la méthode equals() qui retourne un boolean vrai ou faux, il faut utiliser la méthode compareTo() qui se trouve dans l'interface Comparable. La méthode compareTo() permet d'obtenir un ordre naturel avec les objets comparables. Par exemple, si l'on a plusieurs objets Personne qui ont un attribut âge, il serait intéressant de pouvoir les comparer à l'aide de cette caractéristique. Cette méthode retourne -1 si un objet a est plus petit que b, retourne 0 si un objet a est égal à b et retourne 1 si un objet a est plus grand que l'objet b.

Le comparable est utilisé pour établir un ordre naturel entre les instances d'un même type d'objet. Pour que le tout fonctionne, quelques étapes sont nécessaires :

  1. Implémenter la classe Comparablepublic class Figure implements Comparable {
  2. Définir la méthode compareTo(Object o) là où c'est nécessaire (dans ce cas-ci, la classe Figure)
  3. Dans une classe tierce, créer des instances dans un tableau et appeler la méthode Arrays.sort(t)

On peut préciser la précision d'une comparaison en multipliant les valeurs comparables. Dans le code suivant, une précision de 1% est appliquée.

public int compareTo(Object o) {
  if (o instanceof Cercle)
    return (int) 100*getSurface() - 100*((Cercle)x).getSurface()));
  else

Comparators

D'autres comparateurs que l'ordre naturel peuvent être définis. Par exemple un étudiant qui peut être ordonné par son nom, ou par sa note moyenne ou bien par son numéro d'étudiant.

public class Etudiant implements Comparable {
	private String num, nom, prenom;
	private double note;
 
	public int compareTo(Object x) {
		if (!(x instance of Etudiant)) throw new ClassCastException("message");
		Etudiant e = (Etudiant) x;
		int res = nom.compareToIgnoreCase(e.nom);
		if (res != 0) return res;
		res = prenom.compareToIgnoreCase(e.prenom);
		return res;
	}
 
	public boolean equals (Object x) {
		if (!(x instance)) return false;
		return (this.compareTo(x)==0);
	}
}

Gestion des événements

  1. Le modèle de délégation
  2. Exemple des événements les plus courants

Les modèles de délégations

Fonctionne selon le modèle source-écouteur. Signifie qu'un composant graphique (bouton) qui est la source de l'événement. L'écouteur a une méthode de gestion de l'événement. Il s'enregistre comme écouteur auprès du bouton (composant/source). La source averti l'écouteur dès que l'événement se produit. Ensuite, l'écouteur exécute la méthode de la gestion de l'événement.

Exemple - Le clic sur un bouton

Le clic est un événement de type Action (rprésenté par la classe java.awt.event.ActionEvent.

L'écouteur d'un événement d'action est un objet, instance d'une classe, qui implémente l'interface ActionListener. Il faut d'abord créer une classe Ecouteur qui implémente l'interface ActionListener.

ActionListener contient une méthode public void actionPerformed(ActionEvent x).

Catégories d'événements :

  1. ActionEvent
  2. KeyEvent
  3. MouseEvent
  4. WindowEvent

Phases de mise en oeuvre d'événements

  1. Créer une classe écouteur qui implémente ActionListener (package java.awt.event)
  2. Créer l'objet sourceJButton b = new JButton(“OK”)
  3. Lier l'objet source à l'écouteur → b.addActionListener(e)
  4. Avertit l'écouteur du clic
  5. Exécution de e.actionPerformed()
public class AppUI extends JFrame implements ActionListener {
 
  public AppUI () {
    // ajout des éléments graphiques
    miNouveau.addActionListener(this);
    miOuvrir.addActionListener(this);
  }
 
  // ...
 
  public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
 
    if (source == miNouveau) {
      // instructions
    }
    else if (source == miOuvrir) {
      // instructions
    }
  }
}

WindowListener et WindowsAdapter

L'interface WindowListener contient plusieurs méthodes qui doivent être implémentés par les classes qui implémentent cette interface.

  1. WindowClosed()
  2. WindowClosing()
  3. WindowActivated()
  4. WindowsDeactivated()
  5. WindowIconified()
  6. WindowDeconified()

Dans les applications, toutes ces méthodes ne sont pas nécessaires. On peut donc utiliser WindowAdapter qui implémente les méthodes vides de l'interface WindowListener. Donc, si notre classe descend de WindowAdapter, seulement les méthodes nécessaires seront implémentées.

Modèle Vue Contrôleur

Les applications créés en Java, devraient être implémentés avec le MVC (modèle, vue, contrôleur). Le modèle étant les classes d'objet (Figure, Cercle, Rectangle, Dessin, etc), les vues étant les interfaces graphiques qui affichent ou recuillent les informations sur les classes (modèle) et les contrôleurs qui incluent la gestion des événements. Les vues et les contrôleurs peuvent être regroupés en paires (ou plus) dans les mêmes classes (ex : ApplicationDessinUI).

Il y a donc trois types de classes selon ce modèle :

  1. Les classes du modèle → Les classes qui implémentent les traitements au sein de l'entreprise (la logique métier ou business rules), comprend aussi les threads.
  2. Les classes de la vue → Les classes de présentation qui affichent tout ou une partie du modèle à l'écran (ex: tout ce qui est swing)
  3. Les classes contrôleur
    • Connectent et déconnectent le modèle de la vue
    • Opèrent les événements (écouteurs)

Modèle MVC avec écouteur

Modèle observeur / observable

Les classes du modèle peuvent aussi implémenter l'interface Observable, c'est à dire qu'il y peut y avoir plusieurs vues qui seront alors mis à jour systématiquement lorsqu'une modification se fait à la classe observée. Les vues implémentent l'interface Observer et qui lui permettra justement d'observer une classe observable.

La connexion entre le modèle et la vue est rédigée à l'aide du patron de conception Observer. Pour mettre en place le patron, l'API Java fournit :

  1. l'interface Observer
  2. l'interface Observable

Observable

Quelques méthodes peuvent être implémentées :

  • setChanged()
  • notifyObservers()
  • addObserver(Objet vue) → connecte la vue au modèle
  • removeObserver(Object view)

Modèle observeur/observable

Observer

La principale méthode d'un observeur est utile quand les données du modèle ont changé et c'est → void update() qui fera la mise à jour de l'affichage selon les nouvelles données.

Ressources externes

developpement/java/introduction.txt · Dernière modification : 2023/10/03 23:12 de sgariepy