
Aujourd'hui j'inaugure un genre nouveau avec de petites exercices en rapport avec Java, dans l'objectif de mieux comprendre les rouages et les particularités du langage.
Nous allons donc voir qu'avec une mauvaise conception d'une classe toute simple, il est possible de "casser" le principe encapsulation, si chère à la POO, et qui permet à une instance de classe de protéger ses attributs d'éventuelles modifications externes...
Prenons donc la classe suivante dont le code complet se trouve ci-dessous :
import java.util.Calendar; import java.util.Date; public final class MyObject { private final Date date; private final Number number; public MyObject(Date date, Number number) { // On vérifie que la date soit plus récente que le 1 janvier 2000 : Calendar cal = Calendar.getInstance(); cal.set(2000, java.util.Calendar.JANUARY, 1); if (cal.before(date)) { throw new IllegalArgumentException("'date' doit être supérieur à l'an 2000"); } this.date = date; // On vérifie que l'objet Number soit bien un nombre positif if (number.intValue() < 0) { throw new IllegalArgumentException("'number' doit correspondre à un nombre positif !"); } this.number = number; } public void hello() { System.out.println("Date : " + this.date); System.out.println("Number : " + this.number.intValue()); System.out.println(); } }
Il s'agit donc d'une classe finale (qui ne peut donc pas être redéfinie), qui dispose de deux attributs à visibilités privés. Ces deux attributs sont également finaux (ils ne peuvent pas être réassignés par la suite), et leurs valeurs sont vérifiées lors de la construction de l'objet, selon les règles suivantes :
java.util.Date, doit correspondre à une date venant après le 1er janvier 2000.java.lang.Number, doit correspondre à un nombre avec une valeur entière positive.Bien entendu, la classe ne possède pas de méthode permettant de modifier ses attributs, mais simplement une méthode hello() qui permet d'afficher la valeur de ces attributs.
Enfin, le code s'exécutera dans un environnement protégé par un SecurityManager, ce qui fait qu'on ne pourra utiliser la méthode "barbare" setAccessible(true) de l'API de Réflection pour enfreindre les règles d'encapsulations.
L'objectif sera donc de modifier la valeur des attributs de la classe MyObject et de leurs donner des valeurs incorrects, le tout sans toucher au code de cette dernière.
L'exercice se basera sur le code suivant :
public class Main { // La méthode main() ne doit pas être modifié ! public static void main(String[] args) throws Exception { // On défini un SecurityManager : System.setSecurityManager(new SecurityManager()); // Et on appelle la méthode exercice() : exercice(); } // Exemple de base : public static void exercice() { Date date = new Date(); // Date actuelle Number number = new Integer(100); // 100 MyObject obj = new MyObject(date, number); obj.hello(); obj.hello(); } }
Donc les questions sont :
En modifiant uniquement le code de la méthode exercice(), comment faire en sorte que les deux appels à obj.hello() affichent des valeurs différentes pour la même instance de l'objet ?
On pourra pour cela utiliser toutes les classes et méthodes de l'API standard, et créer autant de nouvelles classes ou méthodes que neccessaire.
L'idéal serait d'obtenir un résultat de la forme suivante :
Date : Mon Nov 03 17:19:08 CET 2008 Number : 100 Date : Thu Jan 01 01:00:00 CET 1970 Number : -100
Existe-t-il dans l'API standard des classes qui ne posent pas ce genre de problème lorsqu'elles sont utilisées comme attribut ?
Comment corriger la classe MyObject pour corriger ce problème ?
Alors ? Avez-vous trouvé l'origine du problème ?
Note 1 : Il faut bien sûr appeler la méthode hello() de la classe MyObject !
Note 2 : La date du premier janvier 1970 correspond tout simplement à un new Date(0L)
Note 3 : La solution pour la date est la plus évidente, mais dans les deux cas la solution est très proche.
obj.hello();
<b>date.setTime(0L);</b>
obj.hello();
<code>.
public MyObject(Date date, Number number) {
// On vérifie que la date soit plus récente que le 1 janvier 2000 :
Calendar cal = Calendar.getInstance();
cal.set(2000, java.util.Calendar.JANUARY, 1);
if (cal.before(date)) {
throw new IllegalArgumentException("'date' doit être supérieur à l'an 2000");
}
this.date = new Date(date.getTime());
// On vérifie que l'objet Number soit bien un nombre positif
if (number.intValue() < 0) {
throw new IllegalArgumentException("'number' doit correspondre à un nombre positif !");
}
this.number = createNumberCopy(number);
}
public static void exercice() {
Date date = new Date(); // Date actuelle
AtomicInteger number = new AtomicInteger(100); // 100
MyObject obj = new MyObject(date, number);
obj.hello();
date.setTime(0L); // 1er janvier 1970
number.set(-100);
obj.hello();
}
if (number.intValue() < 0) {
throw new IllegalArgumentException("'number' doit correspondre à un nombre positif !");
}
this.number = new Integer(number.intValue());
this.number = new Integer(number.intValue());
if (this.number.intValue() < 0) {
throw new IllegalArgumentException("'number' doit correspondre à un nombre positif !");
}
class RandomNumber extends Number {
@Override
public int intValue() {
// Nombre aléatoire entre -5000 et 5000
return (int) ( (Math.random() * 10000) - 5000 );
}
// ...
}Vous devez être identifié pour poster un commentaire.

Ce blog est mis à disposition sous un contrat Creative Commons BY-NC-SA.
System.gc() a encore frappé : jre 1.5, tomcat 6.0 et multi processeurs
Tutoriels Java SE
Tutoriels Java EE
Sélection du blog
Java SE/EE et Web en général
| Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |