TP de Java

On rappelle que la programmation Java est basée sur la manipulation ... Ces
classes se définissent par des relations d'héritage, toute classe Java hérite ...

Part of the document


TP de Java
Héritage et Polymorphisme
_____________________ Sujets abordés dans ce TP :
Mise en ?uvre de l'héritage
Construction d'objets dérivés
Redéfinition des données et méthodes
Compatibilité ascendante, instanceof
Ligature dynamique
Classes abstraites et interfaces
Chargement dynamique de pile 1) Introduction Dans ce TP nous vous proposons de développer les concepts P.O.O en Java :
l'héritage et le polymorphisme. On rappelle que la programmation Java est
basée sur la manipulation d'objets composés de données membres et de
méthodes. Les objets sont des instances de classes correspondant à des
descriptions d'ensembles d'objets ayant des structures de données communes
et disposant des mêmes méthodes. Ces classes se définissent par des
relations d'héritage, toute classe Java hérite implicitement de la classe
racine Object. De même, ces différentes classes sont structurées sous forme
de packages correspondant à un regroupement de classes, sous un
identificateur commun (correspondant au nom du package). 2) Héritage Mise en ?uvre de l'héritage Une relation d'héritage entre classes se définit en Java à l'aide du mot
clé extends. Le code suivant donne un exemple d'héritage entre deux classes
« vides » : class I1 {}
class I2 extends I1 {} En Java, l'héritage multiple n'est pas permis : une classe ne peut hériter
que d'une seule autre classe tout au plus. Evidemment, l'intérêt de
l'héritage en P.O.O réside dans la re-exploitation des données membres et
des méthodes de la classe mère dans la classe dérivée. Cette re-
exploitation est le plus souvent contrôlée par des droits d'accès entre les
classes mères et les classes dérivées. En Java, il existe en fait quatre déclarations de droit d'accès
définissables dans une classe pour les données membres et les méthodes :
private, pas de déclaration, protected, et public. Les déclarations
protected et public concernent « majoritairement » la définition de
package, elles seront abordées plus en détails dans les TP suivants. En ce
qui concerne les relations d'héritage, seules les déclarations private et
pas de déclaration sont utilisées. Implémenter le code suivant :
class A1{
private int u; int v; void set1(int u)
{setB(); this.u = u;}
void set1(int u, int v)
{setB(); this.u = u; this.v = v;}
int get1() {return u;} private int back;
private void setB() {back = u;}
void undo() {u = back;}
void print1()
{System.out.println(u+";"+v);}
} class A2 extends A1 {
private int w; void set2(int u, int v, int w)
{set1(u); this.v = v; this.w = w;}
int get2() {return w;} void print2()
{System.out.println(get1()+";"+v+";"+w);}
} class A3 extends A2{
void print3()
{System.out.println(get1()+";"+v+";"+get2());}
} Le mettre en ?uvre de la façon suivante : A2 my2 = new A2();
my2.set2(1,2,3);my2.set2(4,5,6);
my2.v = 8; my2.undo();
my2.print2();
A3 my3 = new A3();
my3.set1(7); my3.v = 8;
my3.print3(); Cet exemple met en ?uvre les droits d'accès aux données membres et méthodes
via des relations d'héritage.
. private : Dans cet exemple, la donnée membre u a été déclarée à l'aide
du mot clé private dans la classe A1. Ceci signifie qu'elle est
accessible uniquement par les méthodes de la classe A1. La relation
d'héritage entre les classes A1 et A2 ne lève en rien l'encapsulation
de la donnée membre u dans la classe A2. Il en est de même pour la
méthode setB() de la classe A1, elle est uniquement accessible par les
méthodes de la classe A1. Ce constat est également vrai en ce qui
concerne les classes A1 et A3, et A2 et A3.
. pas de déclaration : En l'absence de déclaration les méthodes et
données membres de la classe A1 sont directement accessibles par la
classe dérivée A2 : v, get1(). De même, cette propriété se reconduit
entre les classes A1 et A3 (v, get1()), et A2 et A3(v, get1(),get2()).
Ces données membres et méthodes sont également accessibles à
l'extérieure des classes. On peut en effet les invoquer à partir d'un
objet instance et de l'opérateur ., comme par exemple : my.v,
my.print(). Construction d'objets dérivés Un objet donné, instance d'une classe dérivée peut donc exploiter les
données membres et les méthodes définies dans la classe mère de cette
classe dérivée. Ceci induit que cet objet instance de la classe dérivée est
lié à un objet instance de la classe mère. Durant la construction d'un
objet dérivé, il y a donc construction des objets pères associés à cet
objet dérivé par les différentes relations d'héritage entre classes. On se
propose d'étudier ici la construction des objets dérivés, implémenter et
mettre en ?uvre le code suivant : class A {
A() {System.out.println("A");}
}
class B extends A {
B() {System.out.println("B");}
}
class C extends A {
C() {System.out.println("C");}
}
class D extends C {
D() {System.out.println("D");}
}
class E extends C {
E()
{System.out.println("E");}
}
A travers ces différentes classes, vous avez redéfini les constructeurs par
défaut. Indiquer le diagramme d'héritage de cet exemple, que pouvez vous
conclure sur le processus de construction des objets dérivés en ce qui
concerne l'ordre de la construction. On se propose de vérifier cet ordre en
ce qui concerne la construction et l'initialisation des données membres
d'une classe. Implémenter les classes suivantes, les mettre en ?uvre,
commenter : class V1 {
int v;
int v1=1; V1() {print1();v=v1;print1();} void print1()
{System.out.println(v+";"+v1+";?");} } class V2 extends V1 {
int v2=2; V2() {print2();v=v2;print2();} void print2()
{System.out.println(v+";"+v1+";"+v2);}
} En Java, un objet dérivé doit impérativement prendre en charge la
construction de l'objet père. Cette prise en charge est assurée par l'appel
des constructeurs de l'objet père via le mot clé super. Implémenter et
mettre en ?uvre le code suivant : class S1 {
int v; S1() {v=1;}
S1(boolean t) {v=2;} void print()
{System.out.println(v);} } class S2 extends S1 {
S2()
{print();}
} class S3 extends S1 {
S3()
{super(true);print();}
} Dans cet exemple, vous avez redéfini dans la classe S1 le constructeur par
défaut. Vous pouvez voir dans la mise en ?uvre de la classe S2 que ce
constructeur par défaut est également appelé par défaut dans une relation
d'héritage. Que pouvez vous conclure sur la prise en charge de la
construction de l'objet père via le mot clé super. Redéfinition des données membres et des méthodes, surcharge de
méthodes Lorsque qu'une classe dérivée déclare des méthodes et des données membres
de même signature que celles d'une de ses classes mères, on dit que la
classe dérivée redéfinit les données membres et les méthodes. Cette
signature correspond en ce qui concerne les données membres à
{nomVariable}, et en ce qui concerne les méthodes {nomMéthode, arguments
effectifs}. Implémenter le code suivant : class R1{
int u; char v='b'; void set(int u)
{this.u = u;} void print()
{System.out.println(v);}
} class R2 extends R1{
char u='a'; void set(int u)
{System.out.println("R2 class");} void print(char w)
{System.out.println(u+","+v+","+w);}
} Le mettre en ?uvre de la façon suivante : R1 my1 = new R1();
my1.set(2);
System.out.println(my1.u);
R2 my2 = new R2();
my2.set(2);
System.out.println(my2.u);
my2.print();
my2.print('c'); A travers cet exemple, vous pouvez voir que la donnée membre u et la
méthode set(int) ont été redéfinies de la classe R1 à la classe R2. De même, vu que la classe dérivée hérite des méthodes de la classe mère la
notion de surcharge de méthodes reste vraie dans une relation d'héritage.
Par exemple la méthode print(char) de la classe R2 correspond à la méthode
surchargée print() de la classe R1. La redéfinition des données membres et des méthodes dans la classe dérivée
est possible de par leur autorisation d'accès par la classe mère. Dans le
cas où ces données membres et ces méthodes sont déclarées privées, leur
déclaration est ignorée par la classe dérivée. Implémenter le code suivant
: class RP1{
private int i=0; private void ic() {i++;} void update1()
{System.out.println(i);ic();}
} class RP2 extends RP1{
char i='a'; void ic() {i++;} void update2()
{System.out.println(i);ic();}
} Le mettre en oeuvre de la façon suivante, commenter : RP2 my = new RP2();
my.update1();
my.update2();
my.ic();
my.update1();
my.update2(); Java permet d'interdire la redéfinition de méthodes et de données membres
dans les classes dérivées à l'aide du mot clé final. Implémenter les
classes suivantes, relever et commenter les erreurs de la compilation de ce
code. class RF1 {
final int i=0;
final void ic() {}
} class RF2 extends RF1{
char i='a';
void ic() {i++;}
} 3) Polymorphisme Introduction, compatibilité ascendante, instanceof Le polymorphisme est un des concepts important de la P.O.O, fortement