En licence pro MI AW
Par Olivier Bailleux, Maître de conférences HDR, Université de bourgogne
comp-20-1
Complétez le constructeur de la classe suivante, qui représente un points dans un repère à deux dimensions.
public class Point
{
private double x;
private double y;
public Point(double x, double y)
{
// À compléter
}
}
public Point(double x, double y)
{
this.x = x; this.y = y;
}
comp-20-2
Réécrivez le constructeur à deux paramètres de la classe Point sans utiliser this
.
public Point(double abscisse, double ordonnee)
{
x = abscisse; y = ordonnee;
}
comp-20-3
Soit la méthode main
suivante :
public static void main(String[] args)
{
Point[] tab = new Point[10];
System.out.println(tab[5]);
}
Y-a-t-il une erreur à la compilation ? À l’exécution ? Que contient la cellule tab[5]
? Qu’est-ce qui s’affiche à l’exécution, si applicable ?
Aucune erreur à la compilation ni à l’exécution. La cellule tab[5]
(comme toutes les autres) contient la valeur null
qui indique qu’elle n’a encore reçu aucune valeur. Affichage à l’exécution : null
.
comp-20-4
Comment faire pour ajouter un point de coordonnées (5.2, 3.7) dans la cellule tab[5]
?
tab[5] = new Point(5.2, 3.7);
comp-20-5
Soit la méthode main
suivante :
public static void main(String[] args)
{
ArrayList<Point> tab = new ArrayList<>();
System.out.println(tab.get(5));
}
Y-a-t-il une erreur à la compilation ? À l’exécution ? Que contient la cellule d’indice 5 de tab
? Qu’est-ce qui s’affiche à l’exécution, si applicable ?
Pas d’erreur à la compilation, mais erreur à l’exécution car la liste désignées par tab
est vide, donc la cellule d’indice 5 n’existe pas.
comp-20-6
Comment faire pour ajouter un point de coordonnées (5.2, 3.7) en position 5 dans la liste désignées par tab
?
On ne peut pas tant qu’il n’y a rien aux positions 0, 1, 2, 3 et 4. Si ces chacune de ces cellules contiennent des références de Point
ou la valeur null
, alors on peu écrire :
tab.add(5, new Point(5.2, 3.7));
comp-21
Soit la classe suivante représentant un polygone constitué d’une liste de points.
public class Polygone
{
private ArrayList<Point> liste;
// À compléter
}
Réalisez le constructeur par défaut de cette classe.
public Polygone()
{
liste = new ArrayList<>();
}
On peut aussi écrire :
this.liste = new ArrayList<>();
this.liste = new ArrayList<Point>();
comp-22-1
Réalisez une méthode retournant le nombre de sommets (points) du polygone courant.
public int nbSommets()
{
return liste.size();
}
comp-22-2
La méthode suivante devrait permettre d’ajouter un nouveau point au polygone…
public static void addSommet(Point p)
{
liste.add(p);
}
… mais elle comporte une erreur. Expliquez l’erreur et donnez une version corrigée.
Une méthode de classe ne peut accéder au attributs de l’instance courante car cette méthode peut être appelée sans être associée à une instance et même s’il n’existe aucune instance de la classe concernée.
public void addSommet(Point p)
{
liste.add(p);
}
comp-23-1
On tente d’utiliser la classe Polygone
de la manière suivante…
public static void main(String[] args)
{
Polygone p;
p.addSommet(new Point(12.5,5.6));
// ...
}
…mais il y a une erreur. Indiquez quelle est cette erreur et donner une version corrigée.
La variable p
n’est pas initialisée. Pour pouvoir utiliser la méthode d’instance addSommet
, il faut une instance de la classe Polygone
avec laquelle appeler cette méthode.
public static void main(String[] args)
{
Polygone p = new Polygone();
p.addSommet(new Point(12.5,5.6));
// ...
}
comp-23-2
La méthode main
suivante compile-t-elle sans erreur ? Si oui, y a-t-il une erreur à l’exécution ? Si non, que se passe-t-il à l’exécution ?
public static void main(String[] args)
{
new Polygone().addSommet(new Point(12.5,5.6));
}
Le programme compile et s’exécute sans erreur. Une instance de Polygone
est créée en mémoire dans le tas et un point est ajouté au polygone représenté par cette instance. Mais la référence de cette instance de Polygone
est perdue, on ne pourra rien en faire et elle sera recyclée. l’exécution du programme ne produit aucune affichage.
comp-24-1
On déclare et initialise la variable de méthode suivante :
ArrayList<int> liste = new ArrayList<>();
Mais le programme ne compile pas. Il y a une erreur. Laquelle ? Pourquoi ? Comment résoudre le problème ?
Le type int
est un type primitif, ce qui signifie que les données de ce type ne sont pas des objets mais de simple valeurs en mémoire. La classe ArrayList
ne permet pas de créer des liste de valeurs de types primitifs. Mais on peut utiliser à la place des classes enveloppes. La classe enveloppe qui encapsule une donnée de type int
est la classe Integer
.
ArrayList<Integer> r = new ArrayList<>();
comp-24-2
Réalisez une méthode de classe acceptant un paramètre w
désignant une instance de ArrayList
représentant une liste d’entier et retournant l’instance de ArrayList
obtenue en ajoutant 0 à la fin de la liste désignée par w
. Attention, la liste passée en paramètre ne doit pas être modifiée.
Voici une solution possible.
public static ArrayList<Integer> ajoute0(ArrayList<Integer> w)
{
ArrayList<Integer> r = new ArrayList<>();
for(int x : w)
{
r.add(x);
}
r.add(0);
return r;
}
Il existe d’autres solution mais dans tous les cas il est nécessaire de créer une nouvelle instance de ArrayList<Integer>
et de recopier dans la nouvelle liste désignée par w
, puis d’y ajouter la valeur 0.
La variante suivant utilise le constructeur en copie de la classe ArrayList
:
public static ArrayList<Integer> ajoute0(ArrayList<Integer> w)
{
ArrayList<Integer> r = new ArrayList<>(w);
r.add(0);
return r;
}
comp-24-3
Réalisez une méthode main
qui crée une liste vide de type ArrayList<Integer>
, ajoute les valeur 4 et 15 dans cette liste, puis appelle la méthode ajoute0
de la question précédente et affiche la liste retournée par cette méthode.
public static void main(String[] args)
{
ArrayList<Integer> t = new ArrayList<>();
t.add(4); t.add(15);
ArrayList<Integer> u = ajoute0(t);
System.out.println(u);
}
comp-25-1
La classe Integer
est non modifiable. Ceci signifie qu’un objet de type Integer
ne peut être modifié après sa création. Donnez l’affichage réalisé par l’excécution de la méthode main
suivante (qui, pour information, compile sans erreur).
public static void main(String[] args)
{
Integer x = 34;
x = x + 1;
System.out.println(x);
}
La valeur affichée est 35. Si vous vous êtes étonnés, passez à la question suivante. Et si vous n’êtes pas étonnés, passez aussi à l’exercice suivant.
comp-25-2
Soit la méthode main
suivante :
public static void main(String[] args)
{
Integer x = 34; // Ligne A
x = x + 1; // ligne B
System.out.println(x);
}
Pourquoi, alors que la classe Integer
n’est pas modifiable, peut-on ajouter 1 à x
?
En ligne A, on crée une instance de Integer
qui contient la valeur 34. La variable x
ne contient pas 34. Elle contient la référence de cette instance contenant 34.
En ligne B, on crée une nouvelle instance de Integer
contenant 35. La référence de cette nouvelle instance remplace dans x
la référence de l’ancienne instance contenant 34. L’ancienne instance n’est plus référencée et la mémoire qu’elle utilise sera recyclée par le ramasse-miette de Java.
comp-25-3
L’affirmation suivante est elle correcte :
Toute classe dotée d’un constructeur en copie est modifiable.
C’est faux. Des classes modifiables et des classes non modifiables peuvent avoir des constructeurs en copie.
comp-25-4
Que faut-il rechercher dans la documentation des méthodes d’une classe pour déterminer si cette classe est modifiable ?
Il faut rechercher des méthodes permettant de modifier l’instance courante, c’est à dire la valeur d’au moins un de ses attributs ou de tout objet désigné par au moins un de ses attributs.
comp-25-5
Quel est le principal avantage et quel est le principal inconvénient de l’utilisation de classes non modifiables ?
- Avantage : on supprime les effets de bord, c’est à dire la possibilité que la modification d’un objet désigné par une variable impacte l’objet désigné par une autre variable.
- Inconvénient : Obtenir une version modifiée d’un objet ne peut se faire qu’en la reconstruisant complètement, ce qui consomme plus de ressources de mémoire et de ressources CPU qu’une simple modification.
comp-26-1
Dessinez la configuration mémoire immédiatement après l’exécution de la ligne A de le méthode main
suivante:
public static void main(String[] args)
{
Polygone poly1 = new Polygone(); // Ligne A
// ...
}
comp-26-2
Dessinez la configuration mémoire immédiatement après l’exécution de la ligne B de le méthode main
suivante:
public static void main(String[] args)
{
Polygone poly1 = new Polygone();
Point q = new Point(1.0, 2.0); // Ligne B
// ...
}
comp-26-3
Dessinez la configuration mémoire immédiatement après l’exécution de la ligne C de le méthode main
suivante:
public static void main(String[] args)
{
Polygone poly1 = new Polygone();
Point q = new Point(1.0, 2.0);
Point[] tab = new Point[2]; // Ligne C
// ...
}
comp-26-4
Dessinez la configuration mémoire immédiatement après l’exécution de la ligne D de le méthode main
suivante:
public static void main(String[] args)
{
Polygone poly1 = new Polygone();
Point q = new Point(1.0, 2.0);
Point[] tab = new Point[2];
tab[0] = q;
tab[1] = q; // Ligne D
// ...
}
comp-26-5
Dessinez la configuration mémoire immédiatement après l’exécution de la ligne E de le méthode main
suivante:
public static void main(String[] args)
{
Polygone poly1 = new Polygone();
Point q = new Point(1.0, 2.0);
Point[] tab = new Point[2];
tab[0] = q;
tab[1] = q;
poly1.addSommet(tab[0]);
poly1.addSommet(tab[1]); // Ligne E
// ...
}
comp-26-6
Dessinez la configuration mémoire immédiatement après l’exécution de la ligne F de le méthode main
suivante:
public static void main(String[] args)
{
Polygone poly1 = new Polygone();
Point q = new Point(1.0, 2.0);
Point[] tab = new Point[2];
tab[0] = q;
tab[1] = q;
poly1.addSommet(tab[0]);
poly1.addSommet(tab[1]);
Polygone poly2 = poly1; // Ligne F
}
comp-27-1
Pour mémoire, voici l’attribut de la classe Polygone
.
public class Polygone
{
private ArrayList<Point> liste = new ArrayList<>();
//...
}
Voici la définition d’un constructeur en copie de la classe Polygone
.
public Polygone(Polygone m)
{
this();
for(Point p : m.liste)
{
liste.add(p);
}
}
Ce constructeur es copie est il satisfaisant dans le cas où la classe Point
est modifiable ? Et si elle est non modifiable ?
Dans le cas ou Point
est non modifiable, il n’y a pas de problème. Mais si elle est modifiable, on risque un effet de bord, i.e. une modification d’un point dans le polygone créé en copie affecterait le polygone original, et réciproquement.
comp-27-2
En supposant que la classe Point
dispose d’une méthode de clonage appelée clone
, réalisez un constructeur en copie de Polygone
respectant les bonnes pratiques de programmation (i.e., effectuant une copie en profondeur).
public Polygone(Polygone m)
{
this();
for(Point p : m.liste)
{
liste.add(p.clone());
}
}
Si on utilisait une constructeur en copie de Point
, il faudrait remplacer p.clone()
par new Point(p)
.
comp-28-1
Soit la classe Main
suivante :
public class Main
{
public static void printTabInt(int[] t)
{
for(int i=0; i< t.length; i++)
{
System.out.print(t[i] + " ");
}
System.out.println();
}
public static void main(String[] args)
{
int[] tab1 = {1, 2, 3, 4, 5};
int[] tab2 = tab1; // Ligne A
printTabInt(tab2);
printTabInt(tab1);
}
}
La ligne A provoque-t-elle une erreur à la compilation ? Une erreur à l’exécution ? Si le programme s’exécute, donnez les affichage réalisés.
Il n’y a aucune erreur et l’exécution du programme affiche :
1 2 3 4 5
1 100 3 4 5
comp-28-2
On conserve la même classe que pour la question précédente, mais on modifie la méthode main
qui devient :
{
int[] tab1 = {1, 2, 3, 4, 5};
int[] tab2 = tab1; // Ligne A
tab2[1] = 100; // Ligne B
printTabInt(tab2);
printTabInt(tab1);
}
Donnez les affichages réalisés.
1 100 3 4 5
1 100 3 4 5
Les deux variables désignent le même tableau, qui est modifié par la ligne B, qui, en quelque sorte, impacte la donnée désignée par la variable tab1
. Il s’agit d’un effet de bord.
comp-28-3
On considère à présent une version modifiée de la classe Main
précédente, dans laquelle les tableaux de int
ont été remplacé par des tableau de Integer
:
public class Main
{
public static void printTabInt(Integer[] t)
{
for(int i=0; i< t.length; i++)
{
System.out.print(t[i] + " ");
}
System.out.println();
}
public static void main(String[] args)
{
Integer[] tab1 = {1, 2, 3, 4, 5};
Integer[] tab2 = tab1;
tab2[1] = 100;
printTabInt(tab2);
printTabInt(tab1);
}
}
Quels sont les affichages réalisés par l’exécution du programme ?
1 100 3 4 5
1 100 3 4 5
Exactement les mêmes que précédemment. On a toujours un effet de bord. La classe Integer
n’est pas modifiable, mais le problème ici vient du fait qu’un tableau, quel que soit le type de ces cellules, est un objet modifiable.
comp-28-4
Dans la méthode main
suivante…
public static void main(String[] args)
{
int[] tab1 = {1, 2, 3, 4, 5};
int[] tab2 = tab1; // Ligne A
tab2[1] = 100;
printTabInt(tab2); // Méthode d'affichage d'un tableau de int
printTabInt(tab1);
}
La ligne A ne réalise pas une duplication du tableau désigné par tab1
, ce qui entraîne ensuite un effet de bord (qui, dans une application réelle, pourrait être voulu ou au contraire inopiné et source d’un bug).
Comment faire pour réaliser une copie en profondeur qui rende le résultat de la copie complètement indépendant de l’original ?
Avec les connaissances transmises dans le cadre de cet enseignement, on peut réaliser une méthode de duplication d’un tableau.
public static int[] duplique(int[] original)
{
int[] copie = new int[original.length];
for(int i=0; i<original.length; i++)
{
copie[i] = original[i];
}
return copie;
}
La ligne A de la méthode main
doit alors être remplacée par :
int[] tab2 = duplique(tab1);
Mais il est possible d’utiliser la méthode de classe standard Arrays.copyOf
. Vous pouvez vous documenter à ce sujet, qui est hors programme.
comp-28-5
Soit la méthode main
suivante :
public static void main(String[] args)
{
String s1 = "aaaaa";
String s2 = s1; // Ligne A
s2 = s2 + 'X'; // Ligne B
System.out.println(s1);
System.out.println(s2);
}
Donnez les affichages réalisés à l’exécution.
aaaaa
aaaaaX
Il n’y a pas d’effet de bord car la ligne B crée une nouvelle instance de String
.