t
t
t
t
t t 11) Le système d’E/S de Java
tttt
11) Le système d’E/S de Java
Texte original t Traducteur : Armel FORTUN
t
t
///
Ce chapitre contient 10 pages
1 2 3 4 5 6 7 8 9 10
\\\
t t t
t t t
t
t t t
From a design standpoint, things get really weird here. First of all, you might think that because these methods are not part of a base class or the Serializable interface, they ought to be defined in their own interface(s). But notice that they are defined as private, which means they are to be called only by other members of this class. However, you don’t actually call them from other members of this class, but instead the writeObject( ) and readObject( ) methods of the ObjectOutputStream and ObjectInputStream objects call your object’s writeObject( ) and readObject( ) methods. (Notice my tremendous restraint in not launching into a long diatribe about using the same method names here. In a word: confusing.) You might wonder how the ObjectOutputStream and ObjectInputStream objects have access to private methods of your class. We can only assume that this is part of the serialization magic. t D'un point de vue conceptuel, les choses sont vraiment mystérieuses ici. Pour commencer, vous allez penser que parce que ces méthodes ne font pas partie des classes de base ou de l'interface Serializable, elles devraient être définies dans leur propre interface(s). Mais notez qu'elles sont définies comme private, ce qui veux dire qu'elles doivent êtres appelées seulement par les autres membres de cette classe. Cependant, vous ne faites pas réellement appel à elles depuis les autres membres de cette classe, mais à la place des méthodes writeObject() et readObject() des objets ObjectOutputStream et ObjectInputStream appellent vos objets writeObject() et les méthodes readObject(). (Remarquez mon énorme retenue de ne pas ma lancer dans une longue diatribe au sujet de l'emploi des même noms de méthodes ici. En un mot : embrouillant.) Vous pourrez vous demander comment les objets ObjectOutputStream et ObjectInputStream ont accès aux méthodes private de votre class. Nous pouvons seulement supposer que cela fait partie de la magie de la sérialisation.
t t t
In any event, anything defined in an interface is automatically public so if writeObject( ) and readObject( ) must be private, then they can’t be part of an interface. Since you must follow the signatures exactly, the effect is the same as if you’re implementing an interface. t De toute façon, tout ce qui est défini dans une interface est automatiquement public donc writeObject() et readObject() doivent être private, à ce moment là ils feront partie d'une interface. Puisque vous devez suivre exactement les signatures, l'effet est le même que si vous implémentiez une interface.
t t t
It would appear that when you call ObjectOutputStream.writeObject( ), the Serializable object that you pass it to is interrogated (using reflection, no doubt) to see if it implements its own writeObject( ). If so, the normal serialization process is skipped and the writeObject( ) is called. The same sort of situation exists for readObject( ). t Il apparaîtra que lorsque vous appelez ObjectOutputStream.writeObject(), l'objet Serializable que vous lui transmettez est interrogé (utilisant la réflexion, pas de doute) pourvoir si il implémente son propre writeObject(). Si c'est le cas, le processus normale de sérialisation est omis et est le writeObject()appelé. Le même type de situation existe pour readObject().
t t t
There’s one other twist. Inside your writeObject( ), you can choose to perform the default writeObject( ) action by calling defaultWriteObject( ). Likewise, inside readObject( ) you can call defaultReadObject( ). Here is a simple example that demonstrates how you can control the storage and retrieval of a Serializable object: t Il y a une autre entorse. À l'intérieur de votre writeObject( ), vous pouvez choisir d'exécuter l'action writeObject() par défaut en appelant defaultWriteObject(). Également, dans readObject()vous pouvez appeler defaultReadObject(). Voici un exemple simple qui démontre comment vous pouvez contrôler le stockage et la récupération d'un objet Serializable :
t t t
//: c11:SerialCtl.java
// Controlling serialization by adding your own
// writeObject() and readObject() methods.
import java.io.*;

public class SerialCtl implements Serializable {
  String a;
  transient String b;
  public SerialCtl(String aa, String bb) {
    a = "Not Transient: " + aa;
    b = "Transient: " + bb;
  }
  public String toString() {
    return a + "\n" + b;
  }
  private void
    writeObject(ObjectOutputStream stream)
      throws IOException {
    stream.defaultWriteObject();
    stream.writeObject(b);
  }
  private void
    readObject(ObjectInputStream stream)
      throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
    b = (String)stream.readObject();
  }
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    SerialCtl sc =
      new SerialCtl("Test1", "Test2");
    System.out.println("Before:\n" + sc);
    ByteArrayOutputStream buf =
      new ByteArrayOutputStream();
    ObjectOutputStream o =
      new ObjectOutputStream(buf);
    o.writeObject(sc);
    // Now get it back:
    ObjectInputStream in =
      new ObjectInputStream(
        new ByteArrayInputStream(
          buf.toByteArray()));
    SerialCtl sc2 = (SerialCtl)in.readObject();
    System.out.println("After:\n" + sc2);
  }
} ///:~
t
//: c11:SerialCtl.java
// Contrôler la sérialisation en ajoutant vos propres
// méthodes writeObject() et readObject().
import java.io.*;

public class SerialCtl implements Serializable {
  String a;
  transient String b;
  public SerialCtl(String aa, String bb) {
    a = "Not Transient: " + aa;
    b = "Transient: " + bb;
  }
  public String toString() {
    return a + "\n" + b;
  }
  private void
    writeObject(ObjectOutputStream stream)
      throws IOException {
    stream.defaultWriteObject();
    stream.writeObject(b);
  }
  private void
    readObject(ObjectInputStream stream)
      throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
    b = (String)stream.readObject();
  }
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    SerialCtl sc =
      new SerialCtl("Test1", "Test2");
    System.out.println("Before:\n" + sc);
    ByteArrayOutputStream buf =
      new ByteArrayOutputStream();
    ObjectOutputStream o =      new ObjectOutputStream(buf);
    o.writeObject(sc);
    // Maintenant faites les revenir :
    ObjectInputStream in =      new ObjectInputStream(
        new ByteArrayInputStream(
          buf.toByteArray()));
    SerialCtl sc2 = (SerialCtl)in.readObject();
    System.out.println("After:\n" + sc2);
  }
} ///:~
t t t
In this example, one String field is ordinary and the other is transient, to prove that the non-transient field is saved by the defaultWriteObject( ) method and the transient field is saved and restored explicitly. The fields are initialized inside the constructor rather than at the point of definition to prove that they are not being initialized by some automatic mechanism during deserialization. t Dans cet exemple, un champ String est normal et le second est transient, pour prouver que le champ non-transient est sauvé par la méthode defaultWriteObject() et le que le champ transient est sauvé et récupéré explicitement. Les champs sont initialisés dans le constructeur plutôt qu'au point de définition pour prouver qu'ils n'ont pas été initialisés par un certain mécanisme automatique durant la sérialisation.
t t t
If you are going to use the default mechanism to write the non-transient parts of your object, you must call defaultWriteObject( ) as the first operation in writeObject( ) and defaultReadObject( ) as the first operation in readObject( ). These are strange method calls. It would appear, for example, that you are calling defaultWriteObject( ) for an ObjectOutputStream and passing it no arguments, and yet it somehow turns around and knows the reference to your object and how to write all the non-transient parts. Spooky. t Si vous employez le mécanisme par défaut pour écrire les parties non-transient de votre objet, vous devrez appeler defaultWriteObject() comme la première action dans writeObject() et defaultReadObject() comme la première action dans readObject(). Ce sont d'étranges appels à des méthodes. Il apparaît, par exemple, que vous appelez defaultWriteObject() pour un ObjectOutputStream et ne lui passez aucun argument, mais il tourne d'une manière ou d'une autre autour et connaît la référence à votre objet et comment écrire toutes les parties non-transient. Étrange.
t t t
The storage and retrieval of the transient objects uses more familiar code. And yet, think about what happens here. In main( ), a SerialCtl object is created, and then it’s serialized to an ObjectOutputStream. (Notice in this case that a buffer is used instead of a file—it’s all the same to the ObjectOutputStream.) The serialization occurs in the line: t Le stockage et la récupération des objets transient utilisent un code plus familier. Et cependant, pensez à ce qu'il se passe ici. Dans main(), un objet SerialCtl est créé, puis est sérialisé en un ObjectOutputStream. (Notez dans ce cas qu'un tampon est utilisé à la place d'un fichier — c'est exactement pareil pour tout l' ObjectOutputStream.) La sérialisation survient à la ligne :
t t t
o.writeObject(sc);
t o.writeObject(sc);
t t t
The writeObject( ) method
must be examining sc to see if it has its own writeObject( )
method. (Not by checking the interface—there isn’t one—or the
class type, but by actually hunting for the method using reflection.) If it
does, it uses that. A similar approach holds true for readObject( ).
Perhaps this was the only practical way that they could solve the problem, but
it’s certainly strange.
t La méthode writeObject() doit examiner sc pour voir si il possède sa propre méthode writeObject(). (Non pas en contrôlant l'interface — il n'y en a pas — ou le type de classe, mais en recherchant en fait la méthode en utilisant la réflexion.) Si c'est le cas, elle l'utilise. Une approche similaire garde true pour readObject(). Peut-être que c'est la seule réelle manière dont ils peuvent résoudre le problème, mais c'est assurément étrange.
t t t


Versioning


t

Versioning

t t t
It’s possible that you might want
to change the version of a serializable class (objects of the original class
might be stored in a database, for example). This is supported but you’ll
probably do it only in special cases, and it requires an extra depth of
understanding that we will not attempt to achieve here. The JDK HTML documents
downloadable from java.sun.com cover this topic quite
thoroughly.
t Il est possible que vous désiriez changer la version d'une classe sérialisable (les objets de la classe original peuvent être stockés dans un base de donnée, par exemple). Ceci est supporté mais vous devrez probablement le faire seulement dans les cas spéciaux, et cela requiert un profondeur supplémentaire de compréhension qui ne sera pas tenté d'atteindre ici. Les documents HTML du JDK téléchargeables depuis java.sun.com couvrent ce sujet de manière très approfondie.
t t t
You will also notice in the JDK HTML
documentation many comments that begin with:
t Vous pourrez aussi noter dans la documentation HTML du JDK que de nombreux commentaires commencent par :
t t t
Warning: Serialized
objects of this class will not be compatible with future Swing releases. The
current serialization support is appropriate for short term storage or RMI
between applications. ...

t Attention : Les objets sérialisés de cette classe ne seront pas compatibles avec les futures versions de Swing. Le support actuel de la sérialisation est approprié pour le stockage à court terme ou le RMI entre les applications. ...
t t t
This is because the versioning mechanism
is too simple to work reliably in all situations, especially with JavaBeans.
They’re working on a correction for the design, and that’s what the
warning is about.
t Ceci parce que le mécanisme de versionning est trop simple pour fonctionner de manière fiable dans toutes les situations, surtout avec les JavaBeans. Ils travaillent sur un correction de la conception, et c'est le propos de l'avertissement.
t t t


Using persistence


t

Utiliser la persistence

t t t
It’s quite appealing to use
serialization technology to store some of the state of
your program so that you can easily restore the program to the current state
later. But before you can do this, some questions must be answered. What happens
if you serialize two objects that both have a reference to a third object? When
you restore those two objects from their serialized state, do you get only one
occurrence of the third object? What if you serialize your two objects to
separate files and deserialize them in different parts of your
code?
t Il est plutôt attrayant de faire appel à la technologie de la sérialisation pour stocker certains états de votre programme afin que vous puissiez facilement récupérer le programme dans l'état actuel plus tard. Mais avant de de pouvoir faire cela, il faut répondre à certaines questions. Qu'arrive-t-il si vous sérialisez deux objets qui ont tous les deux une référence à un troisième objet ? Quand vous récupérez ces deux objets depuis leur état sérialisé, aurez vous une seule occurrence du troisième objet ? Que ce passe-t-il si vous sérialisez vos deux objets pour séparer les fichiers et les désérialisez dans différentes parties de votre code ?
t t t
Here’s an example that shows the
problem:

t Voici un exemple qui montre le problème :
t t t
//: c11:MyWorld.java
import java.io.*;
import java.util.*;

class House implements Serializable {}

class Animal implements Serializable {
  String name;
  House preferredHouse;
  Animal(String nm, House h) {
    name = nm;
    preferredHouse = h;
  }
  public String toString() {
    return name + "[" + super.toString() +
      "], " + preferredHouse + "\n";
  }
}

public class MyWorld {
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    House house = new House();
    ArrayList  animals = new ArrayList();
    animals.add(
      new Animal("Bosco the dog", house));
    animals.add(
      new Animal("Ralph the hamster", house));
    animals.add(
      new Animal("Fronk the cat", house));
    System.out.println("animals: " + animals);

    ByteArrayOutputStream buf1 =
      new ByteArrayOutputStream();
    ObjectOutputStream o1 =
      new ObjectOutputStream(buf1);
    o1.writeObject(animals);
    o1.writeObject(animals); // Write a 2nd set
    // Write to a different stream:
    ByteArrayOutputStream buf2 =
      new ByteArrayOutputStream();
    ObjectOutputStream o2 =
      new ObjectOutputStream(buf2);
    o2.writeObject(animals);
    // Now get them back:
    ObjectInputStream in1 =
      new ObjectInputStream(
        new ByteArrayInputStream(
          buf1.toByteArray()));
    ObjectInputStream in2 =
      new ObjectInputStream(
        new ByteArrayInputStream(
          buf2.toByteArray()));
    ArrayList animals1 =
      (ArrayList)in1.readObject();
    ArrayList animals2 =
      (ArrayList)in1.readObject();
    ArrayList animals3 =
      (ArrayList)in2.readObject();
    System.out.println("animals1: " + animals1);
    System.out.println("animals2: " + animals2);
    System.out.println("animals3: " + animals3);
  }
} ///:~
t
//: c11:MyWorld.java
import java.io.*;
import java.util.*;

class House implements Serializable {}

class Animal implements Serializable {
  String name;
  House preferredHouse;
  Animal(String nm, House h) {
    name = nm;
    preferredHouse = h;
  }
  public String toString() {
    return name + "[" + super.toString() +
      "], " + preferredHouse + "\n";
  }
}

public class MyWorld {
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    House house = new House();
    ArrayList  animals = new ArrayList();
    animals.add(
      new Animal("Bosco the dog", house));
    animals.add(
      new Animal("Ralph the hamster", house));
    animals.add(
      new Animal("Fronk the cat", house));
    System.out.println("animals: " + animals);

    ByteArrayOutputStream buf1 =
      new ByteArrayOutputStream();
    ObjectOutputStream o1 =      new ObjectOutputStream(buf1);
    o1.writeObject(animals);
    o1.writeObject(animals); // Écrit un 2éme jeu
    // Écrit vers un flux différent :
    ByteArrayOutputStream buf2 =
      new ByteArrayOutputStream();
    ObjectOutputStream o2 =      new ObjectOutputStream(buf2);
    o2.writeObject(animals);
    // Now get them back:
    ObjectInputStream in1 =      new ObjectInputStream(
        new ByteArrayInputStream(
          buf1.toByteArray()));
    ObjectInputStream in2 =      new ObjectInputStream(
        new ByteArrayInputStream(
          buf2.toByteArray()));
    ArrayList animals1 =
      (ArrayList)in1.readObject();
    ArrayList animals2 =
      (ArrayList)in1.readObject();
    ArrayList animals3 =
      (ArrayList)in2.readObject();
    System.out.println("animals1: " + animals1);
    System.out.println("animals2: " + animals2);
    System.out.println("animals3: " + animals3);
  }
} ///:~
t t t
One thing that’s interesting here is that it’s possible to use object serialization to and from a byte array as a way of doing a “deep copy” of any object that’s Serializable. (A deep copy means that you’re duplicating the entire web of objects, rather than just the basic object and its references.) Copying is covered in depth in Appendix A. t Une chose intéressante ici est qu'il est possible d'utiliser la sérialisation d'objet depuis et vers un tableau de bytes comme une manière de faire une « copie en profondeur » de n'importe quel objet qui estSerializable. (une copie en profondeur veux dire que l'on copie la structure complète des objets, plutôt que seeulement l'objet de base et ses références.) La copie est abordée en profondeur dans l'Annexe A.
t t t
Animal objects contain fields of type House. In main( ), an ArrayList of these Animals is created and it is serialized twice to one stream and then again to a separate stream. When these are deserialized and printed, you see the following results for one run (the objects will be in different memory locations each run): t Les objets Animal contiennent des champs de type House. Dans main(), une ArrayList de ces Animals est crée et est sérialisée deux fois vers un flux et ensuite vers un flux distinct. Quand ceci est désérialisé et affiché, on obtient les résultats suivant pour une exécution (les objets seront dans des emplacements mémoire différents à chaque exécution) :
t t t
animals: [Bosco the dog[Animal@1cc76c], House@1cc769
, Ralph the hamster[Animal@1cc76d], House@1cc769
, Fronk the cat[Animal@1cc76e], House@1cc769
]
animals1: [Bosco the dog[Animal@1cca0c], House@1cca16
, Ralph the hamster[Animal@1cca17], House@1cca16
, Fronk the cat[Animal@1cca1b], House@1cca16
]
animals2: [Bosco the dog[Animal@1cca0c], House@1cca16
, Ralph the hamster[Animal@1cca17], House@1cca16
, Fronk the cat[Animal@1cca1b], House@1cca16
]
animals3: [Bosco the dog[Animal@1cca52], House@1cca5c
, Ralph the hamster[Animal@1cca5d], House@1cca5c
, Fronk the cat[Animal@1cca61], House@1cca5c
]
t
animals: [Bosco the dog[Animal@1cc76c], House@1cc769
, Ralph the hamster[Animal@1cc76d], House@1cc769
, Fronk the cat[Animal@1cc76e], House@1cc769
]
animals1: [Bosco the dog[Animal@1cca0c], House@1cca16
, Ralph the hamster[Animal@1cca17], House@1cca16
, Fronk the cat[Animal@1cca1b], House@1cca16
]
animals2: [Bosco the dog[Animal@1cca0c], House@1cca16
, Ralph the hamster[Animal@1cca17], House@1cca16
, Fronk the cat[Animal@1cca1b], House@1cca16
]
animals3: [Bosco the dog[Animal@1cca52], House@1cca5c
, Ralph the hamster[Animal@1cca5d], House@1cca5c
, Fronk the cat[Animal@1cca61], House@1cca5c
]
t t t
Of course you expect that the deserialized objects have different addresses from their originals. But notice that in animals1 and animals2 the same addresses appear, including the references to the House object that both share. On the other hand, when animals3 is recovered the system has no way of knowing that the objects in this other stream are aliases of the objects in the first stream, so it makes a completely different web of objects. t Bien sur vous vous attendez à ce que les objets déserialisés aient des adresses différentes des originaux. Mais notez que dans animals1 et animals2 les mêmes adresses apparaissent, incluant les références à l'objet House que tous les deux partagent. D'un autre coté, quand animals3 est récupéré le système n'a pas de moyen de savoir que les objets de l'autre flux sont des alias des objets du premier flux, donc il crée un réseau d'objets complétement différent.
t t t
As long as you’re serializing everything to a single stream, you’ll be able to recover the same web of objects that you wrote, with no accidental duplication of objects. Of course, you can change the state of your objects in between the time you write the first and the last, but that’s your responsibility—the objects will be written in whatever state they are in (and with whatever connections they have to other objects) at the time you serialize them. t Aussi longtemps que vos sérialisez tout dans un flux unique, vous pourrez récupérer le même réseau d'objets que vous avez écrits, sans aucune duplication accidentelle d'objets. Bien sûr, vous pouvez modifier l'état de vos objets entre la période d'écriture du premier et du dernier, mais c'est de votre responsabilité — les objets seront écrit dans l'état où ils sont quel qu'il soit (et avec les connexions quelles qu'elles soient qu'ils ont avec les autres objets) au moment ou vous les sérialiserez.
t t t
The safest thing to do if you want to save the state of a system is to serialize as an “atomic” operation. If you serialize some things, do some other work, and serialize some more, etc., then you will not be storing the system safely. Instead, put all the objects that comprise the state of your system in a single container and simply write that container out in one operation. Then you can restore it with a single method call as well. t La chose la plus sûre à faire si vous désirez sauver l'état d'un système est de sérialiser comme une opération « atomique. » Si vous sérialisez quelque chose, faites une autre action, et en sérialisez une autre en plus, etc., alors vous ne stockerez pas le système sûrement. Au lieu de cela, mettez tous les objets qui comprennent l'état de votre système dans un simple conteneur et écrivez simplement ce conteneur à l'extérieur en une seule opération. Ensuite vous pourrez aussi bien le récupérer avec un simple appel à une méthode.
t t t
The following example is an imaginary computer-aided design (CAD) system that demonstrates the approach. In addition, it throws in the issue of static fields—if you look at the documentation you’ll see that Class is Serializable, so it should be easy to store the static fields by simply serializing the Class object. That seems like a sensible approach, anyway. t L'exemple suivant est un système imaginaire de conception assistée par ordinateur (CAD) qui démontre cette approche. En plus, il projette dans le sujet des champs static — si vous regardez la documentation vous verrez que Class est Serializable, donc il sera facile de stocker les champs static en sérialisant simplement l'objet Class. Cela semble comme une approche sensible, en tous cas.
t t t
//: c11:CADState.java
// Saving and restoring the state of a
// pretend CAD system.
import java.io.*;
import java.util.*;

abstract class Shape implements Serializable {
  public static final int
    RED = 1, BLUE = 2, GREEN = 3;
  private int xPos, yPos, dimension;
  private static Random r = new Random();
  private static int counter = 0;
  abstract public void setColor(int newColor);
  abstract public int getColor();
  public Shape(int xVal, int yVal, int dim) {
    xPos = xVal;
    yPos = yVal;
    dimension = dim;
  }
  public String toString() {
    return getClass() +
      " color[" + getColor() +
      "] xPos[" + xPos +
      "] yPos[" + yPos +
      "] dim[" + dimension + "]\n";
  }
  public static Shape randomFactory() {
    int xVal = r.nextInt() % 100;
    int yVal = r.nextInt() % 100;
    int dim = r.nextInt() % 100;
    switch(counter++ % 3) {
      default:
      case 0: return new Circle(xVal, yVal, dim);
      case 1: return new Square(xVal, yVal, dim);
      case 2: return new Line(xVal, yVal, dim);
    }
  }
}

class Circle extends Shape {
  private static int color = RED;
  public Circle(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}

class Square extends Shape {
  private static int color;
  public Square(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
    color = RED;
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}

class Line extends Shape {
  private static int color = RED;
  public static void
  serializeStaticState(ObjectOutputStream os)
      throws IOException {
    os.writeInt(color);
  }
  public static void
  deserializeStaticState(ObjectInputStream os)
      throws IOException {
    color = os.readInt();
  }
  public Line(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}

public class CADState {
  public static void main(String[] args)
  throws Exception {
    ArrayList shapeTypes, shapes;
    if(args.length == 0) {
      shapeTypes = new ArrayList();
      shapes = new ArrayList();
      // Add references to the class objects:
      shapeTypes.add(Circle.class);
      shapeTypes.add(Square.class);
      shapeTypes.add(Line.class);
      // Make some shapes:
      for(int i = 0; i < 10; i++)
        shapes.add(Shape.randomFactory());
      // Set all the static colors to GREEN:
      for(int i = 0; i < 10; i++)
        ((Shape)shapes.get(i))
          .setColor(Shape.GREEN);
      // Save the state vector:
      ObjectOutputStream out =
        new ObjectOutputStream(
          new FileOutputStream("CADState.out"));
      out.writeObject(shapeTypes);
      Line.serializeStaticState(out);
      out.writeObject(shapes);
    } else { // There's a command-line argument
      ObjectInputStream in =
        new ObjectInputStream(
          new FileInputStream(args[0]));
      // Read in the same order they were written:
      shapeTypes = (ArrayList)in.readObject();
      Line.deserializeStaticState(in);
      shapes = (ArrayList)in.readObject();
    }
    // Display the shapes:
    System.out.println(shapes);
  }
} ///:~
t
//: c11:CADState.java
// Sauve et récupère l'état de la
// simulation d'un système de CAD.
import java.io.*;
import java.util.*;

abstract class Shape implements Serializable {
  public static final int
    RED = 1, BLUE = 2, GREEN = 3;
  private int xPos, yPos, dimension;
  private static Random r = new Random();
  private static int counter = 0;
  abstract public void setColor(int newColor);
  abstract public int getColor();
  public Shape(int xVal, int yVal, int dim) {
    xPos = xVal;
    yPos = yVal;
    dimension = dim;
  }
  public String toString() {
    return getClass() +
      " color[" + getColor() +
      "] xPos[" + xPos +
      "] yPos[" + yPos +
      "] dim[" + dimension + "]\n";
  }
  public static Shape randomFactory() {
    int xVal = r.nextInt() % 100;
    int yVal = r.nextInt() % 100;
    int dim = r.nextInt() % 100;
    switch(counter++ % 3) {
      default:
      case 0: return new Circle(xVal, yVal, dim);
      case 1: return new Square(xVal, yVal, dim);
      case 2: return new Line(xVal, yVal, dim);
    }
  }
}

class Circle extends Shape {
  private static int color = RED;
  public Circle(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}

class Square extends Shape {
  private static int color;
  public Square(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
    color = RED;
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}

class Line extends Shape {
  private static int color = RED;
  public static void
  serializeStaticState(ObjectOutputStream os)
      throws IOException {
    os.writeInt(color);
  }
  public static void
  deserializeStaticState(ObjectInputStream os)
      throws IOException {
    color = os.readInt();
  }
  public Line(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}

public class CADState {
  public static void main(String[] args)
  throws Exception {
    ArrayList shapeTypes, shapes;
    if(args.length == 0) {
      shapeTypes = new ArrayList();
      shapes = new ArrayList();
      // Ajoute des références aux objets de class :
      shapeTypes.add(Circle.class);
      shapeTypes.add(Square.class);
      shapeTypes.add(Line.class);
      // Fait quelques formes :
      for(int i = 0; i < 10; i++)
        shapes.add(Shape.randomFactory());
      // Établit toutes les couleurs statiques en GREEN:
      for(int i = 0; i < 10; i++)
        ((Shape)shapes.get(i))
          .setColor(Shape.GREEN);
      // Sauve le vecteur d'état :
      ObjectOutputStream out =        new ObjectOutputStream(
          new FileOutputStream("CADState.out"));
      out.writeObject(shapeTypes);
      Line.serializeStaticState(out);
      out.writeObject(shapes);
    } else { // C'est un argument de ligne de commande
      ObjectInputStream in =        new ObjectInputStream(
          new FileInputStream(args[0]));
      // Read in the same order they were written:
      shapeTypes = (ArrayList)in.readObject();
      Line.deserializeStaticState(in);
      shapes = (ArrayList)in.readObject();
    }
    // Affiche les formes :
    System.out.println(shapes);
  }
} ///:~
t t t
The Shape class implements Serializable, so anything that is inherited from Shape is automatically Serializable as well. Each Shape contains data, and each derived Shape class contains a static field that determines the color of all of those types of Shapes. (Placing a static field in the base class would result in only one field, since static fields are not duplicated in derived classes.) Methods in the base class can be overridden to set the color for the various types (static methods are not dynamically bound, so these are normal methods). The randomFactory( ) method creates a different Shape each time you call it, using random values for the Shape data. t La classe Shape implemente Serializable, donc tout ce qui est hérité de Shape est aussi automatiquement Serializable. Chaque Shape contient des données, et chaque classe Shape dérivée contient un champ static qui détermine la couleur de tous ces types de Shapes. (Placer un champ static dans la classe de base ne donnera qu'un seul champ, puisque les champs static ne sont pas reproduit dans les classes dérivés.) Les méthodes dans les classes de base peuvent être surpassées [overridden] pour établir les couleurs des types variables (les méthodes static ne sont pas dynamiquement délimitées, donc ce sont des méthodes normales). La méthode randomFactory() crée un Shape différent chaque fois que vous y faites appel, utilisant des valeurs aléatoires pour les données du Shape.
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel