Exercices Java : deroulement et etude d exécution d'un programme - boucle for et while

Dans cet exemple, nous allons suivre pas à pas l’exécution d’un programme. Les instructions d’un programme peuvent changer la mémoire, c’est à dire les valeurs des variables, et avoir des effets, notamment via les entrées-sorties écran-clavier. Les déclarations pour leur part, ont pour conséquence de créer de nouvelles variables. Nous allons retracer dans un tableau synthétique l’évolution pas à pas d’un programme, en notant l’effet produit par les déclarations et instructions. Ce tableau va comporter une première colonne référant aux numéros de ligne du programme. Une colonne servira à noter la valeur des conditions des instructions conditionnelles et des boucles. Ensuite, il y aura une colonne pour chaque variables et pour finir une colonne pour les entrées clavier et une colonne pour les sorites à l’écran. Nous noterons qu’une variable n’est pas définie en inscrivant le sigle NEP (pour N’Existe Pas) dans la colonne correspondante. Un point d’interrogation est utilisé quand la variable existe (elle a été déclarée),mais elle n’a pas été initialisée.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Exemple5_0 {
publics tatic void main ( String [ ] args ) {
int total = 0 ;
int x ;
Terminal . ecrireString ( " Entrez le multiplicateur : " ) ;
x = Terminal . lireInt ( ) ;
for ( int i =1 ; i 4; i ++){
total = total + ( i x ) ;
}
Terminal . ecrireString ( "La somme des 4 premier smultiples est : " ) ;
Terminal . ecrireInt ( total ) ;
Terminal . sautDeLigne ( ) ;
}
}

Et voici le tableau qui retrace l’exécution de ce programme :

nbtesttotalxiclavierécran
4
5
6
7
8
8.init
8.test
9
8.modif
8.test
9
8.modif
8.test
9
8.modif
8.test
9
8.modif
8.test
10
11
12
13

true

true

true

true

false

NEP
0
0
0
0
0
0
3
3
3
9
9
9
18
18
18
30
30
30
30
30
30
30
NEP
NEP
?
?
NEP
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
NEP
NEP
NEP
NEP
NEP
1
1
1
2
2
2
3
3
3
4
4
4
5
5
NEP
NEP
NEP
NEP
3

Entrez le multiplicateur :

La somme des 4 premiers multiples est :

La première ligne du tableau montre l’état initial avant exécution. {sidebar id=8}
L’instruction for a trois morceaux intervenant à des moments différents, c’est pourquoi on adistingué es effets de l’initialisation, du test de la condition iLa ligne 10 est prise en compte en ceci que l’accolade ferme un bloc, ce qui met fin à l’existence de la variable i qui est locale à ce bloc. On aurait pu de même prendre en compte la ligne 14 qui met fin aux variables total et x.
Un tel tableau relate une exécution donnée. Si on change la valeur x entrée ligne 7, il faut changer tout le tableau.
On voit sur cet exemple que le corps de la boucle, la ligne 9, est exécutée 4 fois au cours du programme.

Exercice 1 : déroulement d’une boucle for

Avec un tableau comme celui donné dans l’exemple 5.0, retracez une exécution du programme suivant dans laquelle on entre au clavier la valeur 5

1
2
3
4
5
6
7
8
9
10
11
12
class Exo5_1{
public static void main ( String [ ] args ) {
int x ;
Termin a l . ecrireString ( "Un entier svp : " ) ;
x = Terminal . lireInt ( ) ;
for ( int i = 0 ; i 4 ; i ++){
Terminal . ecrireInt ( x+ i ) ;
Terminal . sautDeLign e ( ) ;
}
Terminal . ecrireStringln ( " Fin i " ) ;
}
}

Exercice 2 : déroulement d’une boucle while

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Exo6_2{
public stati c void main ( String [ ] args ) {
int puis = 1 ;
int x , res ;
Terminal . ecrireString ( "Un entier svp : " ) ;
x = Terminal . lireInt ( ) ;
res = x ;
while ( r e s 1000){
res = res x ;
puis = puis +1 ;
}
Terminal . ecrireString ( "Le résultat est " ) ;
Terminal . ecrireInt ( puis ) ;
Terminal . sautDeLigne ( ) ;
}
}

1. Que calcule ce programme ?
2. Avec un tableau comme celui donné dans l’exemple 5.0, retracez une exécution du programme suivant dans laquelle on entre au clavier la valeur 8.

Exercice 3 : étude de boucles (nombre d’itérations)

Considérez les boucles suivantes :{sidebar id=1}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/ / Boucle 1
inti = 5 ;
while ( i >0) { Terminal . ecrireStringln ( " Test " ) ;
i ??;
}
/ / Boucle 2
int i = 0 ;
while ( i >0) { Terminal . ecrireStringln ( ‘ ‘ Test ’ ’ ) ;
i ??;
}
/ / Boucle 3
for ( int i = 1 ; i != 5 ; i = i + 2 ) {
Te rmin a l . ecrireStringln ( " Test " ) ;
}
/ / Boucle 4
for ( i n t i = 1 ; i >0 ; i ++) {
Te rminal . ecrireStringln ( " Te s t " ) ;
}
/ / Boucle 5
int i = 1 ;
while ( i 100) {
Terminal . ecrireStringln ( i i ) ;

1. Combien de messages “Test” affichent les boucles 1, 2, 3 et 4 ?
2. Écrivez une boucle for équivalente à la boucle 1 (c’est à dire qui s’exécute de façon identique).
3. Combien de messages affiche la boucle 3 si on change int i = 1 ; en int i = 0 ; ?
4. Combien de tours réalise la boucle 5 ?
5. Quels sont les messages affichés par :

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Boucle6 {
public static void main ( Stringargs [ ] ) {
int i = 0 ;
int j = 0 ;
for ( i = 1 ; i 6 ; i ++) {
if ( i > 4 ) { break ;
} else {
j = j + i ;
Terminal . ecrireStringln ( " j = " + j + " i = " + i ) ;
}
}
Terminal . ecrireStringln ( " Fin j = " + j + " i = " + i ) ;
}}

Exercice JAVA masquage (shadowing) et héritage

But:

 Indiquer l'affichage du programme. Attention aux situations de masquage.  

Thème:

 Masquage (Shadowing), Héritage  

Fichiers:

 Alphabet.java

Vous trouverez ci-dessous le programme Alphabet qui implémente la hiérarchie de 3 classes (A, B et C). Indiquez l'affichage du programme (sans l'exécuter). Attention aux situations de shadowing.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273class Alphabet { public static void main(String args[]) { A[] as = new A[3]; as[0] = new A(1); as[1] = new B(2); as[2] = new C(3); for (int i = 0; i as.length; i++) { as[i].afficherClasse(); System.out.println("-----"); } for (int i = 0; i as.length; i++) { as[i].afficherVariables(); System.out.println("-----"); } }}class A { protected int a = 5; public A(int a) { this.a = a; } public void afficherClasse() { System.out.println("Classe A"); } public void afficherVariables() { System.out.println("a = " + a); }}class B extends A { protected int b = 6; public B(int b) { super(2 * b); a = b; } public void afficherClasse() { super.afficherClasse(); System.out.println("Classe B"); } public void afficherVariables() { super.afficherVariables(); System.out.println("b = " + b); }}class C extends B { protected int b = 7; protected int c = 8; public C(int c) { super(3 * c); b = c; } public void afficherClasse() { super.afficherClasse(); System.out.println("Classe C"); } public void afficherVariables() { super.afficherVariables(); System.out.println("c = " + c); }}

L'affichage correct et donné ci-dessous.

Classe A-----Classe AClasse B-----Classe AClasse BClasse C-----a = 1-----a = 2b = 6-----a = 9b = 6c = 8

La difficulté principale réside dans l'affichage du dernier objet (de type C).

Pourquoi l'attribut b vaut-il 6 et non pas 3 ?

Réponse : à cause du masquage. En effet, si on observe la hiérarchie de classes donnée, on peut remarquer qu'un objet de type C possède en fait deux attributs b : l'un hérité de B et l'autre redéfini dans C (qui masque le premier).

Lorsque la méthode afficherVariables va être invoquée sur l'objet de type C, elle va appeler la méthode afficherVariables de la super-classe (B). Cette dernière va afficher l'attribut b de la super-classe, qui vaut 6.

Pour bien comprendre la nuance, remplacez la méthode afficherVariables de la classe C par la suivante :

public void afficherVariables() { super.afficherVariables(); System.out.println("b (masquant) = " + b); System.out.println("c = " + c); }

Ré-exécutez le programme. L'affichage devrait alors être le suivant:

Classe A-----Classe AClasse B-----Classe AClasse BClasse C-----a = 1-----a = 2b = 6-----a = 9b = 6b (masquant) = 3c = 8-----

Exercice polymorphisme et récursivité expressions arithmétiques

But:
Révisions: Comparaison d'une approche procédurale avec une approche orientée objet
Thème:
Polymorphisme, Récursivité
Fichiers:
Arith.java

Johnny C. est étudiant en première année à l'EPFL. Il doit écrire un programme Java simple permettant d'évaluer des expressions arithmétiques. Ces expressions sont constituées de nombres, d'additions et de multiplications; comme ceci :

2 + 5 * 3

Johnny n'a pas encore très bien absorbé les concepts orientés objets permis par Java et il produit la solution suivante :

Arith.java

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051/** Structure de donnee pour les expressions*/class Expression { public int type; public int value; public Expression leftOp; public Expression rightOp; public Expression(int type, int value, Expression leftOp, Expression rightOp) { this.type = type; this.value = value; this.leftOp = leftOp; this.rightOp = rightOp; }}/** Classe principale */class Arith { /** Constantes pour representer les types*/ public static final int TYPE_NUMBER = 1; public static final int TYPE_SUM = 2; public static final int TYPE_PROD = 3; public static void main(String [] args) { // construit l'expression 3 + 2 * 5 Expression term = new Expression(TYPE_SUM, 0, new Expression(TYPE_NUMBER, 3, null, null), new Expression(TYPE_PROD, 0, new Expression(TYPE_NUMBER, 2, null, null), new Expression(TYPE_NUMBER, 5, null, null))); System.out.println(evaluate(term)); } /** Evalue recursivement l'expression */ public static int evaluate(Expression term) { switch (term.type) { case TYPE_NUMBER: return term.value; case TYPE_SUM: return evaluate(term.leftOp) + evaluate(term.rightOp); case TYPE_PROD: return evaluate(term.leftOp) * evaluate(term.rightOp); default: return 0; //erreur, ne devrait jamais se produire } }}

Le programme de Johnny fonctionne, mais est écrit dans un style "procédural" plutôt qu'orienté objet. Il peut donc, à cet égard, être amélioré.

  1. Réflechissez à la solution de Johnny et essayez d'expliquer pourquoi elle n'est pas très bonne et comment elle pourrait être améliorée.
  2. Ecrivez une version améliorée (commencez par lire l'intégralité des recommandations avant de commencer à programmer) :
    • Définissez une classe abstraite Expression qui déclarera une méthode abstraite evaluate.
    • Créez des classes distinctes pour chaque type d'expressions (Number, Sum, Product) et faites les hériter de Expression.
    • Vous pouvez éviter de la duplication inutile de code en créeant une classe abstraite intermédiaire BinOp qui contiendra les attributs leftOp et rightOp représentants respectivement les opérandes gauche et droit d'un expression binaire. Faites hériter Sum et Product de Binop.
    • Adaptez la méthode main de la classe principale à votre nouvelle structure.
  3. Pour comparer la solution originale de Johnny à la votre, faites évoluer votre programme dans le sens suivant :
    • Ajouter la possibilité de générer la représentation de votre expression sous la forme d'une chaîne de caractère. Par exemple si votre expression est le nombre 13, cette fonctionalité doit vous retourner la chaîne "13". Si c'est une Sum avec 12 et 13 comme leftOp et rightOp respectivement, la fonctionalité en question devra retourner la chaîne "12 * 13".
    • Ajouter un nouveau type d'expression binaire : Modulo représentant des expressions binaires dont l'évaluation retourne le reste de la division entière de leftOp par rightOp.

Portez un regard critique à votre nouvelle version. Si vous avez dû faire du "copier-coller" de portions de votre code pour ajouter ces facilités, c'est qu'il y a encore des problèmes de conception. Essayer de faire en sorte qu'il n'y ait aucune duplication inutile de code.

Une fois satisfait de votre version, essayez d'ajouter les mêmes facilités au programme de Johnny.

Laquelle des deux versions a été la plus facile à modifier ?

Note : cet exercice a essentiellement pour but d'illustrer l'intérêt du polymorphisme. Afin de nous concentrer sur cet aspect nous nous autoriserons quelques "entorses" au principe d'une bonne encapsulation (privacité des attributs). Une fois que vous aurez réussi à produire une solution polymorphique et à en dégager l'intérêt par rapport au code d'origine, le soin vous est laissé de parfaire votre code en vous concentrant cette fois sur son encapsulation.

Le programme de Johnny fonctionne, mais est écrit dans un style "procédural" plutôt qu'orienté objet. Il peut donc, à cet égard, être amélioré.

  1. Réflechissez à la solution de Johnny et essayez d'expliquer pourquoi elle n'est pas très bonne et comment elle pourrait être améliorée.
  2. Ecrivez une version améliorée (commencez par lire l'intégralité des recommandations avant de commencer à programmer) :
    • Définissez une classe abstraite Expression qui déclarera une méthode abstraite evaluate.
    • Créez des classes distinctes pour chaque type d'expressions (Number, Sum, Product) et faites les hériter de Expression.
    • Vous pouvez éviter de la duplication inutile de code en créeant une classe abstraite intermédiaire BinOp qui contiendra les attributs leftOp et rightOp représentants respectivement les opérandes gauche et droit d'un expression binaire. Faites hériter Sum et Product de Binop.
    • Adaptez la méthode main de la classe principale à votre nouvelle structure.
  3. Pour comparer la solution originale de Johnny à la votre, faites évoluer votre programme dans le sens suivant :
    • Ajouter la possibilité de générer la représentation de votre expression sous la forme d'une chaîne de caractère. Par exemple si votre expression est le nombre 13, cette fonctionalité doit vous retourner la chaîne "13". Si c'est une Sum avec 12 et 13 comme leftOp et rightOp respectivement, la fonctionalité en question devra retourner la chaîne "12 * 13".
    • Ajouter un nouveau type d'expression binaire : Modulo représentant des expressions binaires dont l'évaluation retourne le reste de la division entière de leftOp par rightOp.

Portez un regard critique à votre nouvelle version. Si vous avez dû faire du "copier-coller" de portions de votre code pour ajouter ces facilités, c'est qu'il y a encore des problèmes de conception. Essayer de faire en sorte qu'il n'y ait aucune duplication inutile de code.

Une fois satisfait de votre version, essayez d'ajouter les mêmes facilités au programme de Johnny.

Laquelle des deux versions a été la plus facile à modifier ?

Note : cet exercice a essentiellement pour but d'illustrer l'intérêt du polymorphisme. Afin de nous concentrer sur cet aspect nous nous autoriserons quelques "entorses" au principe d'une bonne encapsulation (privacité des attributs). Une fois que vous aurez réussi à produire une solution polymorphique et à en dégager l'intérêt par rapport au code d'origine, le soin vous est laissé de parfaire votre code en vous concentrant cette fois sur son encapsulation.

 Exercice JAVA modularisation - erreurs fréquentes dans les méthodes

But:
Trouver des erreurs dans le codage de méthodes
Thème:
Modularisation
Fichiers:
ErreursMethodes.java

Chacune des méthodes methode1 à methode8 dans le programme ErreursMethodes.java ci-dessous contient une seule erreur syntaxique. Il s'agit d'erreurs liées à l'utilisation des paramètres, des variables ou de la valeur retournée par la méthode. Il n'y pas d'erreurs dans la méthode main, ni dans la méthode methodeSansErreurs. Trouvez et corrigez les erreurs !

Si vous sollicitez l'aide du compilateur pour trouver les erreurs, sachez qu'il aura de la peine avec ce type de programme où il y a beaucoup d'erreurs de structure. Il peut vous montrer la ligne approximative où se trouve une erreur, mais la solution proposée n'est pas toujours correcte, la liste des erreurs n'est pas toujours complète et les erreurs ne sont pas présentées dans le bon ordre. Dans ce genre de situation, utilisez surtout votre bon sens et vos propres connaissances de la syntaxe de Java.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667class ErreursMethodes { public static void main (String[] args) { int i1 = methode1(); int i2 = methode2(); int i3 = methode3(); int i4 = methode4(); methode5(); methode6(); methode7(); methode8(); } static int methode1 { int a = 0; System.out.println("Méthode 1"); return a; } static int methode2 () { int a = 0; i1 = 10; System.out.println("Méthode 2"); return a; } static int methode3 () { int a = 0; System.out.println("Méthode 3"); } static int methode4 () { String a = "0"; System.out.println("Méthode 4"); return a; } static void methode5 () { double a = 0; System.out.println("Méthode 5"); return a; } static methode6 () { double a = 0; System.out.println("Méthode 6"); return a; } static void methode7 () { int a = 0; double b = 5.5; methodeSansErreur(a); System.out.println("Méthode 7"); } static void methode8 () { int a = 0; String b = "5.5"; methodeSansErreur(a, b); System.out.println("Méthode 8"); } static void methodeSansErreur (int a, double b) { // Cette méthode ne fait rien du tout }}
Fichiers:
ErreursMethodesCorrige.java
  • methode1: Même s'il n'y a pas d'arguments à envoyer à la méthode, il doit y avoir une liste de paramètres dans l'en-tête. Dans ce cas, il faut ajouter des parenthèses vides.
  • methode2: Il y a une référence à la variable i1 qui n'est pas visible dans la méthode methode2. La portée d'une variable est limitée à son bloc de déclaration, dans le cas de i1 à la méthode main.
  • methode3: La dernière instruction exécutée n'est pas return. Cette instruction est obligatoire pour toutes les méthodes qui retourne une valeur, i.e. qui n'a pas le type void dans l'en-tête.
  • methode4: Le type de la valeur retournée n'est pas le même que le type specifié dans l'en-tête de la méthode.
  • methode5: La méthode essaie de retourner une valeur avec return. Cela n'est pas possible si l'en-tête de la méthode est void.
  • methode6: La méthode n'a pas de type pour la valeur retournée. Si la méthode ne retourne pas de valeur, il faut indiquer void dans l'en-tête.
  • methode7: La méthode sansErreur est appelée avec 1 argument tandis qu'elle en demande 2. Le nombre d'arguments doit toujours correspondre au nombre de paramètres.
  • methode8: La méthode sansErreur est maintenant appelée avec 2 arguments, ce qui est correct, mais le type des arguments ne correspond pas au type des paramètres.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970class ErreursMethodesCorrige { public static void main (String[] args) { int i1 = methode1(); int i2 = methode2(); int i3 = methode3(); int i4 = methode4(); methode5(); methode6(); methode7(); methode8(); } static int methode1 () { int a = 0; System.out.println("Méthode 1"); return a; } static int methode2 () { int a = 0; // i1 = 10; System.out.println("Méthode 2"); return a; } static int methode3 () { int a = 0; System.out.println("Méthode 3"); return a; } static int methode4 () { // String a = "0"; int a = 0; System.out.println("Méthode 4"); return a; } static void methode5 () { double a = 0; System.out.println("Méthode 5"); // return a; } static double methode6 () { double a = 0; System.out.println("Méthode 6"); return a; } static void methode7 () { int a = 0; double b = 5.5; methodeSansErreur(a, b); System.out.println("Méthode 7"); } static void methode8 () { int a = 0; // String b = "5.5"; double b = 5.5; methodeSansErreur(a, b); System.out.println("Méthode 8"); } static void methodeSansErreur (int a, double b) { // Cette méthode ne fait rien du tout }}

Exercice POO de base - trouver les erreurs

But:
Trouver l'erreur dans un programme donné
Thème:
POO de base
Fichiers:
Erreurs.java

Le fichier Erreurs.java contient les déclarations de deux classes. La classe Erreurs contient une méthode main et sert à démarrer le programme. La classe GrillePain sert à représenter des grille-pains dans le programme sous forme d'objets dotés de deux variables d'instance: l'année de fabrication et le nombre de tranches que l'appareil peut griller. Il y a également une méthode d'instance afficherVariables que l'on peut appliquer sur un objet de type GrillePain afin d'afficher les valeurs de ses variables d'instance.

  1. Eclipse vous signale des messages d'erreurs à propos de ce programme. Pourquoi? Corrigez-les.
  2. L'affichage lors de l'exécution montre que la variable d'instance nbTranches a la valeur 0 pour les deux grille-pains même si l'on a envoyé une autre valeur à la méthode constructeur. Pourquoi? Corrigez le programme.

Exemple d'exécution du programme (après la première correction):

Objet de type GrillePain avec variables d'instance annee = 1995 et nbTranches = 2Objet de type GrillePain avec variables d'instance annee = 1998 et nbTranches = 4

Code donné:

Erreurs.java
12345678910111213141516171819202122class Erreurs { public static void main(String[] args) { GrillePain g1 = new GrillePain(1995, 2); g1.afficherVariables(); GrillePain g2 = new GrillePain(1998, 4); g2.afficherVariables(); }}class GrillePain { private int annee; private int nbTranches; private GrillePain(int a, int nbTranches) { annee = a; nbTranches = nbTranches; } private void afficherVariables() { System.out.println ("Objet de type GrillePain avec variables d'instance année = " + annee + " et nbTranches = " + nbTranches); }}
Fichiers:
Erreurs.java
    1. Le programmeur a trop utilisé le mot-clé private au point que l'on ne peut même pas accéder à la méthode constructeur depuis l'extérieur de la classe, ni à la méthode afficherVariables(). Il faut rendre ces deux méthodes publiques en supprimant le mot-clé private.
  1. Les variables d'instance (mais pas les variables locales) reçoivent toujours des valeurs par défaut. Si la variable d'instance nbTranches n'est pas initialisée, elle aura donc la valeur 0. Dans la méthode constructeur, il y a une situation de shadowing où le paramètre a le même nom que la variable d'instance. Il faut utiliser l'objet this pour différencier les deux et ainsi affecter la valeur du paramètre nbTranches à la variable d'instance nbTranches.
Erreurs.java
12345678910111213141516171819202122232425/* Version corrigée */class Erreurs { public static void main (String[] args) { GrillePain g1 = new GrillePain(1995, 2); g1.afficherVariables(); GrillePain g2 = new GrillePain(1998, 4); g2.afficherVariables(); }}class GrillePain { private int annee; private int nbTranches; public GrillePain (int a, int nbTranches) { // !!! pas de private annee = a; this.nbTranches = nbTranches; // !!! this } public void afficherVariables() { // !!! pas de private System.out.println ("Objet de type GrillePain avec variables d'instance année = " + annee + " et nbTranches = " + nbTranches); }}

 Exercice placement sans recouvrement - tableaux et modularisation

But:

Placer sans recouvrement des objets linéaires sur une grille carrée

Thème:

tableaux, Modularisation, algorithme

Le but de cet exercice est de placer sans recouvrement (i.e. on ne peut plus utiliser une case déjà utilisée) des objets linéaires sur une grille carrée. Cela pourrait être par exemple une partie d'un programme de bataille navale. Vous considérerez que la grille est de taille 10 x 10.

Dans le fichier Recouvrement.java :

  1. Ecrivez une méthode auxiliaire  :

    static boolean remplitGrille(boolean [][] grille, int ligne, int colonne, char direction, int longueur);dont le rôle est de vérifier si le placement dans une grille (voir ci-dessous) d'un objet de dimension longueur est possible, en partant de la coordonnée (ligne,colonne) et dans la direction définie par direction (Nord, Sud, Est ou Ouest).

    Si le placement est possible, la méthode devra de plus effectuer ce placement (voir ci-dessous la description de la grille).

    La méthode devra indiquer (par la valeur de retour) si le placement a pu être réalisé ou non.

  2. Dans la méthode main
    • Définissez une variable nommée grille, correspondant à un tableau de booléens, de taille fixe, à deux dimensions, avec dim comme paramètre de taille pour chaque dimension (on a donc une grille carrée).
      La valeur true dans une case [i][j] de cette grille représente le fait qu'un (bout d')objet occupe la case de coordonnées (i, i).
    • Utilisez la méthode précédente pour remplir interactivement la grille, en demandant à l'utilisateur de spécifier la position, la taille et la direction des objets à placer.
      Indiquez à chaque fois à l'utilisateur si l'élément a pu ou non être placé dans la grille.
      Le remplissage se termine lorsque l'utilisateur entre une longueur strictement inférieure à 0.
    • Terminer alors en "dessinant" la grille : afficher un '.' si la case est vide et un '#' si la case est occupée.

Remarques :

  • Dans l'interface utilisateur, pour indiquer les positions, utilisez au choix soit les coordonnées de Java  : 0 à taille de la grille -1 (plus facile), soit les coordonnées usuelles (1 à taille de la grille, un peu plus difficile) ,
  • Pensez à effectuer des tests de validité sur les valeurs entrées (débordement du tableau).
  • pour représenter la direction, vous pouvez utiliser un caractère ('N' pour nord. 'S' pour sud, etc...)
  • N'oubliez pas d'initialiser la grille en tout début de programme !

Exemple de fonctionnement :

Entrez la taille (négative pour arrêter le programme): 3Entrez coord. x : 0Entrez coord. y : 0Entrez direction (N,S,E,O) : SPlacement en (0,0) direction S longueur 3 -> Succès#.........#.........#...............................................................................Entrez la taille (négative pour arrêter le programme): 5Entrez coord. x : 2Entrez coord. y : 4Entrez direction (N,S,E,O) : NPlacement en (2,4) direction N longueur 5 -> EchecEntrez la taille (négative pour arrêter le programme): 5Entrez coord. x : 2Entrez coord. y : 4Entrez direction (N,S,E,O) : EPlacement en (2,4) direction E longueur 5 -> Succès#.........#.........#...#####.......................................................................Entrez la taille (négative pour arrêter le programme): -1Merci d'avoir joué!
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171import java.util.Scanner;class Recouvrement { private static Scanner scanner = new Scanner(System.in); public static void main(String[] args) { boolean [][] grille = new boolean [10][10]; initGrille(grille); ajouterElements(grille); System.out.println("\nMerci d'avoir joué!"); } /**  * Essaie de remplir la grille avec un objet de longueur donnée  * dans une direction donnée en partant des coordonnées   * (targetRow, targetCol).   * @param grille  * @param targetRow  * @param targetCol  * @param direction  * @param longueur  * @return true si le placement est réussi et false sinon  */ static boolean remplitGrille(boolean [][] grille, int targetRow, int targetCol, char direction, int longueur) { int deltaRow = 0, deltaCol = 0; int dim = grille.length; // On calcule ici la direction dans laquelle aller depuis // les coordonnées indiquées par l'utilisateur. // Ex: pour aller au nord, il faut monter d'une // ligne en restant sur la // même colonne. switch (direction) { case 'N': deltaRow = -1; deltaCol = 0; break; case 'S': deltaRow = 1; deltaCol = 0; break; case 'E': deltaRow = 0; deltaCol = 1; break; case 'O': deltaRow = 0; deltaCol = -1; break; } // On se place sur la case de départ int tempRow = targetRow; int tempCol = targetCol; // Est-il possible de placer l'élément ? boolean possible = true; // Avant de modifier la grille il faut vérifier s'il est // possible de mettre tout l'objet for (int i = 0; (i longueur && possible); i++) { if ((tempRow 0) || (tempRow >= dim)) { possible = false; } else if ((tempCol 0) || (tempCol >= dim)) { possible = false; } else if (grille [tempRow][tempCol]) { possible = false; } // On se déplace sur la case suivante tempRow += deltaRow; tempCol += deltaCol; } // Le placement est possible, et on peut donc mettre // à jour la grille if (possible) { tempRow = targetRow; tempCol = targetCol; for (int i = 0; i longueur; i++) { grille [tempRow][tempCol] = true; tempRow += deltaRow; tempCol += deltaCol; } } return possible; } /**  * Initialise toutes les cases de la grille à false  * @param grille  */ static void initGrille(boolean[][] grille) { int dim = grille.length; for (int row = 0; row dim; row++) { for (int col = 0; col dim; col++) { grille[row][col] = false; } } } /**  * Permet à l'utilisateur de demander le placement d'objets  * sur la grille.  * @param grille  */ static void ajouterElements(boolean[][] grille) { int col, row; char dir; int length; System.out.print("Entrez la taille " + "(négative pour arrêter le programme): "); length = scanner.nextInt(); // L'utilisateur signifie qu'il veut arrêter // d'introduire des objets en spécifiant une //longueur négative while (length >= 0) { do { System.out.print("Entrez coord. x : "); col = scanner.nextInt(); } while ((col 0) || (col >= grille.length)); do { System.out.print("Entrez coord. y : "); row = scanner.nextInt(); } while ((row 0) ||(row >= grille.length)); do { System.out.print("Entrez direction (N,S,E,O) : "); dir = scanner.next().charAt(0); } while ((dir != 'N') && (dir != 'S') && (dir != 'E') && (dir != 'O')); System.out.print("Placement en (" + col + "," + row + ") direction " + dir + " longueur " + length + " -> "); if (remplitGrille(grille, col, row, dir, length)) { System.out.println("Succès"); afficheGrille(grille); } else { System.out.println("Echec"); } System.out.println(); System.out.print("Entrez la taille " + "(négative pour arrêter le programme): "); length = scanner.nextInt(); } } /**  * Affichage de la grille  * @param grille  */ static void afficheGrille(boolean[][] grille) { int dim = grille.length; for (int row = 0; row dim; row++) { for (int col = 0; col dim; col++) { if (grille[row][col]) System.out.print("#"); else System.out.print("."); } System.out.println(); } }}

Exercice java transformer un programme donné en un programme orienté objet - Banques

But:
Transformer un programme donné en un programme OO
Thème:
Transformation d'un programme non OO en un programme OO, POO de base

Le fichier Banque1.java contient un programme bancaire qui est modularisé sous forme de méthodes auxiliaires. Transformez-le en programme orienté objet sous le nom de Banque2.java en suivant les étapes suivantes :

  • Etudiez le fonctionnement du programme. La banque a 2 clients. Chaque client a un compte privé et un compte d'épargne avec des soldes différents. Le taux d'intérêt d'un compte d'épargne est plus élevé que celui d'un compte privé. Les données de chaque client (nom, ville et soldes) sont affichées avant et après le bouclement des comptes.
  • Réfléchissez aux objets que vous aimeriez utiliser dans votre programme et ajoutez les classes correspondantes. Il peut s'agir d'objets concrets (client, maison, billet, etc.) ou d'objets abstraits (compte, relation bancaire, etc.). N'oubliez pas que la modularisation n'est pas une science exacte. Chaque programmeur décide des classes qu'il trouve utiles pour son programme. C'est souvent l'étape la plus difficile d'un projet de programmation.
  • Transférez le code concernant les objets dans les classes. Utilisez le mot-clé private pour encapsuler les variables et les méthodes d'instance qui ne seront pas utilisées à l'extérieur de la classe. Chaque méthode (auxiliaire ou d'instance) devrait être courte et sans trop d'instructions détaillées. Les identificateurs (noms des variables et des méthodes) devraient être parlants.

Exemple d'exécution du programme:

Données avant le bouclement des comptes:
Client Pedro de Genève
Compte privé: 1000.0 francs
Compte d'épargne: 2000.0 francs
Client Alexandra de Lausanne
Compte privé: 3000.0 francs
Compte d'épargne: 4000.0 francs
Données après le bouclement des comptes:
Client Pedro de Genève
Compte privé: 1010.0 francs
Compte d'épargne: 2040.0 francs
Client Alexandra de Lausanne
Compte privé: 3030.0 francs
Compte d'épargne: 4080.0 francs

Code donné:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556class Banque1 { public static void main(String args[]) { // Données pour tous les comptes privés (taux d'intérêt): double taux1 = 0.01; // Données pour tous les comptes d'épargne (taux d'intérêt): double taux2 = 0.02; // Données pour le premier client (nom et ville): String nom1 = "Pedro"; String ville1 = "Genève"; // Données pour le compte privé du premier client (solde): double solde1PremierClient = 1000.0; // Données pour le compte d'épargne du premier client (solde): double solde2PremierClient = 2000.0; // Données pour le deuxième client (nom et ville): String nom2 = "Alexandra"; String ville2 = "Lausanne"; // Données pour le compte privé du deuxième client (solde): double solde1DeuxiemeClient = 3000.0; // Données pour le compte d'épargne du deuxième client (solde): double solde2DeuxiemeClient = 4000.0; // Afficher les données du premier client: afficherClient(nom1, ville1, solde1PremierClient, solde2PremierClient); // Afficher les données du deuxième client: afficherClient(nom2, ville2, solde1DeuxiemeClient, solde2DeuxiemeClient); // Bouclement du compte privé du premier client: solde1PremierClient = bouclerCompte(solde1PremierClient, taux1); // Bouclement du compte d'épargne du premier client: solde2PremierClient = bouclerCompte(solde2PremierClient, taux2); // Bouclement du compte privé du deuxième client: solde1DeuxiemeClient = bouclerCompte(solde1DeuxiemeClient, taux1); // Bouclement du compte d'épargne du deuxième client: solde2DeuxiemeClient = bouclerCompte(solde2DeuxiemeClient, taux2); // Afficher les données du premier client: afficherClient(nom1, ville1, solde1PremierClient, solde2PremierClient); // Afficher les données du deuxième client: afficherClient(nom2, ville2, solde1DeuxiemeClient, solde2DeuxiemeClient); } static void afficherClient(String nom, String ville, double solde1, double solde2) { // Cette méthode affiche les données du client * System.out.println("Client " + nom + " de " + ville); System.out.println(" Compte privé: " + solde1 + " francs"); System.out.println(" Compte d'épargne: " + solde2 + " francs"); } static double bouclerCompte(double solde, double taux) { // Cette méthode ajoute les intérêts au solde */ double interets = taux * solde; double nouveauSolde = solde + interets; return nouveauSolde; }}

Banque avec des clientes (Niveau 1)

Vous avez bien noté que l'affichage du programme Banque1 ne fait pas de différence entre les clientes et les clients. Ceci est facile à corriger dans la version orientée objets du programme, par exemple en ajoutant une variable d'instance booléenne masculin à la classe Client (si vous en avez une) et en testant sa valeur dans la méthode d'affichage. Modifiez votre programme, par exemple sous le nom de Banque3, pour qu'il affiche "cliente" au lieu de "client" comme suit:

Données avant le bouclement des comptes:
Client Pedro de Genève
Compte privé: 1000.0 francs
Compte d'épargne: 2000.0 francs
Cliente Alexandra de Lausanne
Compte privé: 3000.0 francs
Compte d'épargne: 4000.0 francs
Données après le bouclement des comptes:
Client Pedro de Genève
Compte privé: 1010.0 francs
Compte d'épargne: 2040.0 francs
Cliente Alexandra de Lausanne
Compte privé: 3030.0 francs
Compte d'épargne: 4080.0 francs

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677class Banque2 { public static void main(String[] args) { // Variables locales pour les taux d'intérêts (afin d'éviter de // répéter les mêmes chiffres pour chaque client): double taux1 = 0.01; double taux2 = 0.02; // Construction des deux clients: Client c1 = new Client("Pedro", "Genève", taux1, 1000.0, taux2, 2000.0); Client c2 = new Client("Alexandra", "Lausanne", taux1, 3000.0, taux2, 4000.0); System.out.println("Données avant le bouclement des comptes:"); c1.afficher(); c2.afficher(); // Bouclement des comptes des deux clients: c1.boucler(); c2.boucler(); System.out.println("Données après le bouclement des comptes:"); c1.afficher(); c2.afficher(); }}class Client { private String nom; private String ville; private Compte cpt1, cpt2; public Client(String nom, String ville, double taux1, double solde1, double taux2, double solde2) { this.nom = nom; this.ville = ville; // Construction d'un compte privé: cpt1 = new Compte(taux1, solde1); // Construction d'un compte d'épargne: cpt2 = new Compte(taux2, solde2); } public void afficher() { // Cette méthode affiche les données du client System.out.println(" Client " + nom + " de " + ville); System.out.println(" Compte privé: " + cpt1.getSolde() + " francs"); System.out.println(" Compte d'épargne: " + cpt2.getSolde() + " francs"); } public void boucler() { // Cette méthode boucle les deux comptes du client cpt1.boucler(); cpt2.boucler(); }}class Compte { private double taux; private double solde; public Compte(double taux, double solde) { this.taux = taux; this.solde = solde; } public double getSolde() { return solde; } public void boucler() { // Cette méthode ajoute les intérêts au solde double interets = taux * solde; solde = solde + interets; }}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647class Banque3 { public static void main(String[] args) { // ... comme avant // Construction des deux clients, notez l'argument booléen: Client c1 = new Client("Pedro", "Genève", taux1, 1000.0, taux2, 2000.0, true); // Nouveau Client c2 = new Client("Alexandra", "Lausanne", taux1, 3000.0, taux2, 4000.0, false); // Nouveau // ... comme avant }}class Client { private String nom; private String ville; private Compte cpt1, cpt2; private boolean masculin; // Nouvelle variable d'instance // Méthode constructeur, notez le paramètre booléen: public Client(String nom, String ville, double taux1, double solde1, double taux2, double solde2, boolean masculin) { // Nouveau this.nom = nom; this.ville = ville; cpt1 = new Compte(taux1, solde1); cpt2 = new Compte(taux2, solde2); this.masculin = masculin; // Nouveau } public void afficher() { if (masculin) // Nouvelle instruction if..else System.out.print(" Client "); else System.out.print(" Cliente "); System.out.println(nom + " de " + ville); System.out.println(" Compte privé: " + cpt1.getSolde() + " francs"); System.out.println(" Compte d'épargne: " + cpt2.getSolde() + " francs"); } // ... comme avant}class Compte { // ... comme avant}
class Banque2 {
public static void main(String[] args) {
// Variables locales pour les taux d'intérêts (afin d'éviter de
// répéter les mêmes chiffres pour chaque client):
double taux1 = 0.01;
double taux2 = 0.02;

// Construction des deux clients:
Client c1 = new Client("Pedro", "Genève", taux1, 1000.0, taux2, 2000.0);
Client c2 = new Client("Alexandra", "Lausanne", taux1, 3000.0, taux2, 4000.0);

System.out.println("Données avant le bouclement des comptes:");
c1.afficher();
c2.afficher();

// Bouclement des comptes des deux clients:
c1.boucler();
c2.boucler();

System.out.println("Données après le bouclement des comptes:");
c1.afficher();
c2.afficher();
}
}

class Client {

private String nom;
private String ville;
private Compte cpt1, cpt2;

public Client(String nom, String ville, double taux1, double solde1,
double taux2, double solde2) {
this.nom = nom;
this.ville = ville;
// Construction d'un compte privé:
cpt1 = new Compte(taux1, solde1);
// Construction d'un compte d'épargne:
cpt2 = new Compte(taux2, solde2);
}

public void afficher() {
// Cette méthode affiche les données du client
System.out.println(" Client " + nom + " de " + ville);
System.out.println(" Compte privé: " +
cpt1.getSolde() + " francs");
System.out.println(" Compte d'épargne: " +
cpt2.getSolde() + " francs");
}

public void boucler() {
// Cette méthode boucle les deux comptes du client
cpt1.boucler();
cpt2.boucler();
}
}

class Compte {

private double taux;
private double solde;

public Compte(double taux, double solde) {
this.taux = taux;
this.solde = solde;
}

public double getSolde() {
return solde;
}

public void boucler() {
// Cette méthode ajoute les intérêts au solde
double interets = taux * solde;
solde = solde + interets;
}
}

Exercice JAVA passage de parametres

But:
Que veut dire qu'une référence est passée par valeur ?
Thème:
Passage de parametres


Soit le programme suivant :

1234567891011121314151617class ConcatIncorrecte{ public static void main(String[] args) { String s = "China Blue"; System.out.println(s); concat(s, " Express"); System.out.println(s); } public static void concat(String s, String s2) { s +=s2; }}
  1. Expliquez pourquoi la méthode concatener ne parvient pas à modifier la chaîne s du main (en y concaténant " Express")
  2. Corriger le codage de la méthode concatener et son utilisation dans le main de sorte à ce que l'exécution du programme affiche :China BlueChina Blue Expressau lieu de :China BlueChina Blue
Fichiers:
ConcatIncorrecte.java


Les explications et la correction sont données dans le code ci-dessous :

12345678910111213141516171819202122232425262728293031323334353637383940414243class ConcatIncorrecte{ public static void main(String[] args) { String s = "China Blue"; System.out.println(s); //version incorrecte //concatener(s, " Express"); // version correcte: s = concatener(s, " Express"); System.out.println(s); }/*les raisons du comportement incorrect sont que:  1. les opérations sur les chaines sont non destructives  (créent une autre chaine au lieu d'agir sur la chaine originale)  2. l'objet s est une référence, mais les référence sont passées  par valeur (on peut altérer l'objet référencé, mais pas la référence  elle même):  + crée une nouvelle chaine  la référence de cette nouvelle chaine est affectée s  (on essaie de changer la référence s et l'effet de cette modification n'est que  local)  (revoir l'exemple du cours "méthode auxilliaires et réutilisabilité" avec  les tableaux)*/ /* version incorrecte  public static void concatener(String s,String s2 )  {  s += s2;  }  */ // version corrigée public static String concatener(String s,String s2 ) { s += s2; return s; }}
Article publié le 18 Mars 2010 Mise à jour le Samedi, 17 Décembre 2022 18:08 par BENKIRANE Fatima Ezzahra