Algorithmique et Programmation : Java
Programmation objet avec Java
Note prise par M.VESCO Thomas
Définition :
Type primitif : type associé à des données scalaires
En java il y as 8 types primitifs :
short,
int,
long,
byte,
float,
double,
char,
boolean
Objet : Entité logicielle caractérisée par un emplacement mémoire identifié, un ensemble de propriétés
décrivant l'état de l'objet et un ensemble de fonctionnalités.
Attributs : Informations qui caractérisent l'état d'un objet ou d'une classe. On parle d'attributs d'instance pour
les attributs dont la valeur propre à chaque objet.
On parle d'attribut de classe pour un attribut associé à une classe d'objets dans son ensemble. Un attribut de
classe est introduit par le mot clef static en java.
Méthode : Fonction associée à un objet.
Systématiquement à un paramètre implicite, nommé this en java, qu'est l'objet qui appelle la
méthode.
Associé à un type de retour (ou de résultat) éventuellement vide, indiqué par le mot clef void en java.
Eventuellement associée à des paramètres explicites.
Signature d'une méthode : C'est le "résumé de la structure de méthode" c'est à dire son type de retour, son
nom et la liste de ses éventuels paramètres avec leur type.
Classe : Ensemble d'objets associées aux mêmes attributs et aux méthodes.
On peut voir une classe comme un moule, ce seras la même forme mais pas le même contenu (un moule de
gâteau va donner une forme mais on peut y mettre ce que l'on souhaite).
Instance : Objet de la classe.
Constructeur : "fonction" membre d'une classe qui permet l'initialisation (un objet).
Un constructeur en java porte toujours le nom de classe et n'a pas de type de retour. En revanche, un
constructeur peut avoir des paramètres. Il peut y avoir plusieurs constructeur dans une même classe, ils
portent tous le nom de la classe et se différencient par la liste de leur paramètres.
1 / 22
Protection : L'accès aux attributs, constructeurs et méthodes d'une classe est défini par un niveau de
protection. Il y a plusieurs niveaux de protection. On déclare privés (mot clef private en java) les éléments
non accesibles aux entités extéréieurs à la classe. On déclare publique (mot clef public) les éléments pouvant
être manipulé en dehors de la classe.
Exemple : Définition d'une classe "Point" en java (Création d'un fichier Point.java)
public class Point{
// Attributs
private double abs;
private double ord;
// Méthodes
// Accesseurs
public double getAbs(){
return this.abs; // return abs;
}
public double getOrd(){
return this.ord; // return ord;
}
// Modificateurs
public void setAbs(double a){
this.abs = a; // abs = a
}
/* On aurais également pu faire :
* public void setAbs(double abs){
* this.abs = abs; -> attribut du point qui a appelé la méthode
* }
*/
public void setOrd(double ord){
this.ord = ord;
}
// Constructeur
public Point(){
this.setAbs(0); // ord = 0
this.setOrd(0); // ord = 0
}
public Point(double abs, double ord){
this.setAbs(abs);
this.setOrd(ord);
}
/* On va ajouter dans la classe Point la méthode toString()*/
public String toString(){
2 / 22
/*/* Construit et renvoie une chaine de caractères représentant l'objet
appelant */
String res = "(";
res = res + this.getAbs() +", "+ this.getOrd() + ")";
return res; /* Les get sont optionnel, on pourrais directement faire
this.abs et this.ord*/
}
} // Fin de la classe Point
On va désormais utilisé notre classe Point.
La définition de la classe TestPoint contiendra la méthode main().
public class TestPoint(){
public static void main(String[] args){
Point p1;
Point p2;
p1 = new Point(); // p1 (0,0)
p2 = new Point(2.3, 7.1); // p2 (2.3, 7.1)
p1.setOrd(3.2); // p1 (0, 3.2)
/* On ne peut pas faire p1.abs = 2.5; car l'attribut est privé */
System.out.println("point 1(" + p1.getAbs() + "," + p1.getOrd() + ")");
System.out.println("point 2 : " + p2); /* affichera à l'écran point 2 :
suivie de l'adresse en hexadécimal du point de référence pour p2, mais étant donné
notre fonction String toString() va afficher à l'écran point p2 : suivie du
résultat de l'appel p2.toString() donc : point p2 : (2.3, 7.1) l'appel à
toString() est faite automatiquement par java */
double d = p1.distance(p2);
// Distance est une méthode de la classe Point
System.out.println("distance entre p1 et p2 : " + d);
} // Fin main()
} // Fin de la classe TestPoint
3 / 22
Les tableaux
Les tableaux sont des structures de données permettant de stocker des données de même type uniquement
et est accessible par un indice indiquant la place de la donnée dans le tableau (plusieurs indices pour un
tableau à plusieurs dimension).
En java (comme en C) les indices des tableaux commencent à 0.
Déclaration d'un tableau à une dimension : tableau d'entrées :
int[] tab;
Création du tableau en mémoire :
tab = new int[N]; // Avec N une valeur entière
Exemple d'utilisation :
for(int i=0; i <N; i++){
tab[i] = 2*i+1;
}
N=5 : tab[0] = 1, tab[1] = 3, tab[2] = 5, tab[3] = 7, tab[4] = 9
Tableau à deux dimensions
Les éléments sont répérés par deux indices, le premier pour le numéro de ligne, le deuxième pour le numéro
de colonne de chaque élément.
Déclaration :
int[][] mat;
mat = new int[L][C]
mat[1][2] // Désigne l'élément de la 2ème ligne et de la 3e colonnes (commence à
0)
4 / 22
Exemple :
public class Tab2D(){
public static void main(String[] args){
int[][] mat = new int[4][2];
int entrer = 1;
for (int i=0; i < 4; i++){
for (int j=0; j < 2; j++){
mat[i][j] = entrer++
}
}
}
System.out.println("matrice[2][1] = " + mat[2][1]); // Renvoie 6
}
Tableau après exécution :
|1|2|
|3|4|
|5|6|
|7|8|
for(int i = 0; i < mat.length; i++){
System.out.println("matrice[" + i + "][0]" + mat[i][0]);
}
/*
matrice[0][0] = 1;
matrice[1][0] = 3;
matrice[2][0] = 5;
matrice[3][0] = 7;
*/
for(int i = 0; i < mat.length; i++){
for(int j = 0; j < mat[i].length; j++){
System.out.print(mat[i][j] + " ");
}
}
Affichage :
|1|2|
|3|4|
|5|6|
|7|8|
5 / 22
Chaque tableau dispose d'une donnée donnant sa taille. Cette donnée s'appelle length.
Exemple :
int[] tab = new int[N];
Alors tab.length = N
int[][] mat = new int [L][C];
Alors mat.length = L et mat[i].length = C pour tout i de 0 à L-1
Un tableau peut contenir des objets.
Soit un tableau t défini par :
int[][] t = new int[N][N];
Affichage de la diagonale principal :
for(int i = 0; i < N, i++){
for (int j = 0; j < N; j++){
if(i == j){
System.out.println(t[i][j]);
}
}
}
Autre méthode plus simple :
for(int i = 0; i < N, i++){
System.out.println(t[i][i]);
}
// Diagonale secondaire
int i, j;
for (i = 0, j=N-1; n && j >=0; i++, j-- ){
System.out.println(t[i][j]);
}
for (int i = 0; i < N; i++){
System.out.println(t[i][N-1-i]);
}
6 / 22
Note :
1. int i = 2;
int j;
j = i++; // j va valloir 2
2. int i = 2;
int j;
j = ++i; // j va valloir 3
Les exeptions
Les exceptions permettent de gérer des erreurs susceptibles de survenir dans l'exécution d'un programme et
de prévoir un traitement dans ce cas.
Exemple en Java :
Classe Fraction.
2 attributs :
Le numérateur
Le dénominateur non nul
public class Fraction{
private int num;
private int den;
public Fraction(int num, int den) throws /* Indique que le constructeur peut
donner une exception*/ FractionExeption{
if (den == 0){
throw new FractionExeption /*Création d'un objet de class
FractionException*/("Dénominateur nul");
}
else{
this.num = num;
this.den = den;
}
}
public itn getNum(){
return num;
}
public int getDen(){
return den;
}
public void setNum(int num){
7 / 22
this.num = num;
}
public void setDen(int den) throws FractionExeption{
if (den == 0){
throw new FractionException ("Dénominateur nul");
}
else{
this.den = den;
}
}
public String toString(){
return num + "/" + den;
}
public static void main(String[] args){
Fraction f1, f2;
try{
f1 = new Fraction(5, 2);
f2 = new Fraction(7, 0);
System.out.println(f1);
System.out.println(f2);
}
catch (FractionException fe){
fe.printStackTrace();
System.out.println("Dénominateur = 0");
}
try{
f1 = new Fraction(5, 2);
System.out.println(f1);
}
catch (FractionException fe){
fe.printStackTrace();
System.out.println("dénominateur = 0");
}
try {
f1 = new Fraction(5, 2);
f1.setDen(0);
System.out.println(f1);
}
catch(FractionException fe){
fe.printStackTrace();
System.out.println("dénominateur = 0");
}
}
}
public class FractionException extends Exeception /*La classe FractionExeception
hérite de la classe Exception de java.*/{
// Attribut
String message;
8 / 22
public FractionException(String[] msg){
message = msg;
}
public String toString(){
return ("FractionExeception : ", message);
}
}
Exécution : java Fraction
FractionException : Dénominateur nul
at : Fraction(Fraction.java:22)
at : Fraction.main(Fraction.java:48)
dénominateur = 0
5/2 // Affichage de f1 (2nd bloc try)
FractionException : Dénominateur nul
at: Fraction setDeux(Fraction.java:34)
at : Fraction.main (Fraction.java : 63)
dénominateur = 0
Exception usuelles en java :
NullPointerException (mauvaise utilisation de la mémoire, référence à null, sortie de tableau...)
ArithmeticException (division par 0)
ClassNotFoundException
NoSuchElementException (avec les itérateurs sur les collections)
Les listes chainées
La limite des tableaux : La taille d'un tableaux est définie à la création du tableau et ne doit pas changer.
Pour changer la taille d'un tableau, il faut créer un nouveau tableau avec la nouvelle taille et transférer les
éléments d'un tableua à l'autre.
Les listes chainée : Les listes sont des structures de données dynamiques permettant d'ajouter ou de
supprimer facilement. La différence avec les tableaux est que les éléments d'une liste ne sont pas contigus
en mémoire (les uns derrières les autres).
Une liste chainée est constituée de cellules. Une cellule est une structure de donnée contenant une
information et un lien vers la cellule suivante de la liste. Le type cellule est donc un type récursif.
9 / 22
Représentation d'une liste chainée :
Les opérations de base sur une liste chainée sont :
Créer une liste vide
Tester si une liste est vide
Ajouter un élément en tête de liste
Retirer l'élément en tête de liste
Insérer un élément dans la liste (à une position)
Supprimer un élément de la liste (à une position)
On peut ainsi ajouter ou supprimer des éléments dans une liste sans la redéfinir. L'accès à un élément se fait à
partir de la tête de la liste jusqu'à la cellule contenant l'élément, en suivant les liens d'une cellule à l'autre. On
noteras que le contenu d'une liste est, tout comme les tableaux, de même type en son propre sein.
-> Définition d'une classe Cellule.
Cellule
Contenu : int
Suivant : Cellule
Cellule(a : int)
Cellule(C : Cellule)
+ getContenu() : int
+ getSuivant() : Cellule
+ setContenu(contenu : int) : void
+ setSuivant(suivant : Cellule) : void
toString()
10 / 22
Classe cellule :
public class Cellule{
private int contenu;
private Cellule suivant;
public Cellule(int a){
contenu = a;
suivant = null;
}
public Cellule(Cellule c){
// Construit une Cellule avec le même contenu que c
contenu = c.contenu;
suivant = null;
// acceseurs et modificateurs.
...
public String toString(){
return this.contenu + " -> "
}
}
Classe ListeChainée d'entiers.
public class ListeChainee{
private Cellule tete;
// constructeurs
// Construction d'une liste vide
public ListeChainee(){
tete = null;
}
// Constructeur de copie
public ListeChainee(ListeChainee l){
...
}
// Accesseurs
public Cellule getTete(){
return tete;
}
public int enTete(){
return tete.getContenu();
11 / 22
}
public boolean estVide(){
return tete == null;
}
public void ajouteTete(int contenu){
Cellule nouvelle = new Cellule(contenu);
if (estVide()){
tete = nouvelle;
}else{
nouvelle.suivant = tete;
tete = nouvelle;
}
}
public void retireTete(){
// Retire la tete de la liste supposée non vide
Cellule ancienneTete = tete;
tete = tete.suivant;
ancienneTete.suivant = null;
}
public String toString(){
if (estVide()){
return "nil";
}
Cellule courante = tete;
String result = "";
do {
result += courante.toString();
courante = courante.suivant;
} while(courante != null);
return result + "nil";
}
Utilisation :
public static void main(String[] args){
ListeChainee l = new ListeChaineee();
l.ajouteTete(4);
l.ajouteTete(9);
System.out.println(l); // 9 -> 4 -> nil
l.retireTete();
System.out.prinln(l) // 4 -> nil
}
12 / 22
Les héritages
Définition : Concept fondamental de la POO (Programmation Orienté Objet).
L'héritage permet de construire des classes à partir d'autres classes.
Si une classe B hérite d'une classe A, les objets de B disposent de toutes de toutesles spécifications de A
(attributs, constructeurs, méthodes) en plus des spécifications propres à la classe B.
Si la classe B hérite de la classe A : tout objet de B est un objet de A avec en plus les caractéristiques propres
de B.
Sous définition : Notion de protection : Tout ce qui est privé dans la classe A n'est pas accesible aux objets
de la classe B. Tout ce qui est public est accesible aux objets de B.
Le mode d'accès protégé (protected en java) : Permet aux objets d'une classe héritié d'accéder aux attributs
ou méthodes déclarées protected. Un attribut ou une méthode déclaré protected dans une classe A est
accesible à toutes les classes héritié de A mais reste privé pour les autres classes.
Exemple d'une classe PointCol héritant d'une classe Point :
// Classe Point (même classe point que précédemment)
public class Point{
private int abs;
private int ord;
/*Accesseur et modificateur public ici*/
// Constructeur de la classe
public Point(int abs, int ord){
this.abs = abs;
this.ord = ord;
}
public void affiche(){
System.out.println("abs : " + abs + "ord : " + ord);
}
}
// Classe PointCol héritant de Point
public class PointCol extends Point{
private char couleur;
public PointCol(int abs, int ord, char couleur){
this.setAbs(abs); /*abs et ord sont privés dans Point*/
this.setOrd(ord);
this.couleur = couleur;
}
/* setAbs() et setOrd() sont des modificateurs de la classe point dont dispose
tout objet de la classe PointCol*/
}
13 / 22
Note sur Java :
Le constructeur d'une classe héritié (ou fille) d'une classe mère doit prendre en charge le constructeur de
tout l'objet, y compris de sa partie héritié de la classe mère.
Pour faire appel à un constructeur de la classe mère dans un constructeur de la classe fille, on utilise le mot
clef super suivie d'une liste d'arguments super (arguments éventuels).
Exemple dans la classePointCol :
public PointCol(int abs, int ord, char couleur){
super(abs, ord);
this.couleur = couleur;
}
Si abs et ord sont déclaré protected dans la classe Point :
public class Poit{
protected int abs;
protected int ord;
...
}
Alors un objet de PointCol y aura acccès puisque PointCol hérite de Point.
Exemple : méthode affichePointCol() dans PointCol :
public void affichePointCol(){
System.out.prinln("abs = " + abs + " ord = " + ord + " couleur = " + couleur);
}
Tout objet d'une classe fille a accès aux méthodes (public ou protected) de sa classe mère
Exemple : affichePointCol() :
public void affichePointCol(){
affiche(); // appel de affiche() de la classe Point
System.out.prinln("couleur = " + couleur);
}
Il est possible de redéfinir une méthode de la classe mère dans la classe fille. Cette méthode redéfinie doit
avoir la même signature que la méthode de la classe mère.
14 / 22
Exemple : redéfinition de la méthode affiche() dans la classe PointCol :
public void affiche(){
System.out.println("abs : " + abs + " ord : " + ord + " couleur : " +
couleur);
}
Il est possible dans affiche() de PointCol, d'appeler affiche() de Point en utilisant le mot clef super qui
désigne alors l'objet pour sa partie hérité de Point.
public void affiche(){
super.affiche();
System.out.prinln("couleur = " + couleur);
}
Remarque :
Java n'autorise que l'héritage simple, c'est-à-dire qu'une classe fille hérite d'une et une seule classe
mère.
En java, la classe Object est ancêtre de toutes les autres classes. On l'appel aussi la "super classe". Toute
classe définie en java hérite de la classe Object.
La méthode toString() est une méthode de la classe Object qui construit une chaîne de caractères
composée du nom de la classe de l'object suivie de @ l'adresse en hexadécimal de l'object.
Cette méthode est à redéfinir dans chaque classe.
La méthode equals() de la classe Object.
public boolean equals(Object o){
return this == o;
}
La méthode retourne vrai si les références this et o égales donc désignent le même objet en mémoire (faux
sinon).
Pour tester l'égalité (même valeurs dans les mêmes attributs) de deux objets dans une classe, il faut redéfinir
equals() dans cette classe.
Exemple : Méthode equals() redéfinie dans la classe Point :
public boolean equals(Object o){
Point p = (Point) o;
return (this.abs == p.abs && this.ord == p.ord);
}
15 / 22
Si p1 et p2 sont de type Point, p1 == p2 teste si p1 et p2 désignent le même objet en mémoire
p1.equals(p2) test si p1 et p2 ont la même abscisse et la même ordonnée.
Transtypage entre classe mère et classe fille
Une classe B hérite d'une classe A (B -> A).
A objA = new A();
B objb = new B();
A autreObjA() = objB;v //correct
B autreObjB = objA; // Faux, un objet de A ne peut pas être considéré comme un
objet de B.
B autreObjB = (B) autreObjA; // Correct car autreObjA désigne un objet de B
Typage dynamique (ou signature dynamique) en Java
Exemple : On définit dans la classe Point une méthode affiche() et une méthode identifie() dans la
classe Point.
public void identifie(){
System.out.println("Je suis un Point");
}
public void affiche(){
System.out.println("Coordonées : " + abs + "," + ord);
}
Dans la classe PointCol on redéfinit identifie() mais pas affiche() :
public void identifie(){
System.out.println("Je suis un Point de couleur :" + couleur);
}
Utilisation :
Point p = new Point(3.2, 7.1);
PointCol pc = new PointCol(2.5, 4.3, 'b');
p.affiche(); // affiche() de Point
pc.affiche(); // affiche() de Point car pc.affiche() n'est pas redéfinie dans
PointCol
Affichage :
16 / 22
Je suis un Point
Coordonnée 3.2, 7.1
Je suis un PointCol de couleur b
coordonée 2.5, 4.3
L'appel à identifie() dans la méthode affiche() de Point se fait un fonction du type réel de l'objet
appelant.
Le mot clef final dans le cadre de l'héritage.
Si dans une classe A, une méthode est définie avec le mot clef final, alors cette méthode ne peut pas
être redéfinie dans une classe héritant de A.
Si une classe A est définie avec le mot clef final alors cette classe ne peut pas donner naissance à
d'autres classe par héritage.
Redéfinition de equals() dans PointCol :
public boolean equals(Object o){
if (super.eqals(o)){
PointCol pc = (PointCol) o;
return (this.couleur == pc.couleur);
}
// else
return false;
}
Les classes abstraites en Java
Une classe abstraite est une classe dans laquelle certaine méthodes ne sont pas définies mais données par
leur signature. Une classe abstraite ne peut pas être instanciée c'est à dire qu'on ne peut donc pas créer
d'objet à partir de cette classe. Les classes abstraites donnent naissance à d'autres classes par héritage. Si une
classe héritière ne définit pas toutes les méthodes abstraites (donnée par sa signature dans la classe mère),
elle est encoreune classe abstraite (donc non inistanciable). Une classe héritée devient utilisable si elle a défini
toutes les méthodes abstraites de sa classe mère.
Exemple : Une classe abstraite FormeGeometrique avec un attribut centre ayant une méthode abstraite
surface(). Une classe Triangle ou une classe Rectangle pouvent hériter de cette classe en définissant chacune
une méthode surface().
Une interfaces en Java
Les interfaces ne sont pas liées à l'héritage. Une interface est un ensemble de méthodes données par leur
signature. Une classe qui implémente (mot clef implements) une interface s'image à définir toutes les
méthodes de cette interface. Une interface peut être vue comme un contrat que s'engage à remplir une
classe. Une interface peut être implenter par plusieurs classes. Une classe peut implémenter plusieurs
interfaces (en donnant une définition pour toutes méthodes de chaque interface). La signature des méthodes
dans l'interface doit être respectée dans la définition des méthodes dans chaque classe qui implémente cette
interface.
17 / 22
La générécité en Java
La généricité permet de définir des fonctions et des classes qui dépendant d'un ou plusieurs types générique
c'est à dire non précisés au moment de la définition. C'est au moment de l'utilisation de ces fonctions ou
classes que ces types sont précisés.
18 / 22
Exemple de généricité en java :
public class Exemple<T>{
T valeur;
public Exemple(T valeur){
this.valeur = valeur;
}
public T getValeur(){
return valeur;
}
public void setValeur(T valeur){
this.valeur = valeur;
}
public String toStringt(){
return valeur.toString();
}
}
Remarque : le type T doit être un type classe (et pas un type de base primitif) On peut utiliser les classes
enveloppés de chaque type primitif (Integer pour int, Double pour double...)
Utilisation d'une classe générique :
public class Test{
public static void main(String[] args){
Exemple<String> ex = new Exemple("essai");
Exemple<Integer> ex2 = new Exemple(2);
Exemple<Integer> ex3 = new Exemple("Erreur");
// Erreur à la compilation
// "erreur" n'est pas un Integer
Exemple<Double> ex4;
// Erreur double n'et pas un type classe
Exemple<Double> ex5 = new Exemple(7); // ok
}
}
Classe avec plusieurs types génériques :
public class Paire<T, U>{
T val1;
U val2;
public Paire(T v1; U v2){
val1 = v1;
val2 = v2;
19 / 22
}
public T getVal1(){
return val1;
}
public U getVal2(){
return val2;
}
// Dans main()
Paire<String, Integer> p1 = new Paire("un", 2); // ok
Paire<Integer, String> p2 = new Paire("trois", 4); // erreur
Paire<Integer, String> p3 = new Paire(3, "quatre");
Paire<Integer, Integer> p4 = new Paire(3, 7);
// Fin du dans le main()
}
public class Paire2<T>{
T val1;
T val2;
public Paire2a(T v1, T v2){
val1 = v1;
val2 = v2;
}
}
Utilisation :
Paire2<Integer> p = new Paire2(3, 7);
Généricité et héritage
Soit la classe Paire :
public class Paire<T, U>{
T val1;
U val2;
public void meth1(Paire, <T, U>P){
...
}
public void meth2(Paire<?extends T, ?extends U>P){
...
}
}
Soient les classes Animal et Legume.
20 / 22
Soit la classe Chat qui hérite de Animal, et la classe Carotte qui hérite de Legume.
Paire<Animal, Legume> p1 = new ...;
Paire<Chat, Carotte> p2 = new ...;
p1.meth1(p2); // Erreur
p1.meth2(p2); // ok
Application aux cellules d'une liste
public class CelluleGen<T>{
T contenu;
CelluleGen<T> suivant;
public CelluleGne(T cout){
contenu = cout;
suivant = null;
}
public T getCoutenu(){
return contenu;
}
public CelluleGen<T> getSuivant(){
return suivant;
}
public void setContenu(T cout){
contenu = cout;
}
public void setSuivant(CelluleGen cell){
suivant = cell;
}
public void setSuivant(T cout){
suivant = new CelluleGen(cout);
}
...
}
Classe Liste est générique parce que ses Cellules ont un attribut de type générique. La classe ListeGen
sera générique pour le même type T que la classe CelluleGen
public class ListeGen <T>{
CelluleGen<T> tete;
CelluleGen<T> queue;
public void ajouteTete(T cout){
...
21 / 22
}
public T getContenuTete(){
return tete.getContenu();
}
}
Utilisation dans main()
ListeGen<String> l = new ListeGen();
l.ajouteTete("un");
l.ajouteTete(2); // Erreur
ListeGen<Integer> l2 = new ListeGen();
l2.ajouteTete(2); // ok
La classe Polynome utiliseras ListeGen pour le type spécifique Monome (plus besoin de cast d'Object vers
Monome).
public class Polynome{
ListeGen<Monome> lMonomes;
}
22 / 22