boîte de conserveEn java, n’importe quel objet implémentant l’interface Serializable peut être converti facilement en une séquence d’octets. Cette même séquence peut ensuite être reconverti en l’objet original. Ce mécanisme est par exemple utilisé pour transmettre des objets par RMI.

Voici une petite méthode pour vérifier que votre classe est bien sérialisable

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
 
public class Assert {
    /**
     * Assert that an object is serializable by dumping it and then reading it
     * back and finally by checking the original object and the dumped and read
     * object are equals.
     *
     * <p>Obviously, the method <code>equals()</code> has to be implemented
     * correctly for this method can work.
     * @param objOriginal the instance which will be checked against serialization;
     * must implement <code>equals()</code> or it won't work
     */
    public static void assertSerializable(Object objOriginal) {
        try {
            final ByteArrayOutputStream stream = new ByteArrayOutputStream();
            final ObjectOutputStream out = new ObjectOutputStream(stream);
            out.writeObject(objOriginal);
            out.close();
 
            final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(stream.toByteArray()));
            final Object objSerialized = in.readObject();
            assertEquals(objOriginal, objSerialized);
        } catch (IOException e) {
            fail("unable to serialize or deserialize object " + objOriginal + ": " + e.getMessage());
        } catch (ClassNotFoundException e) {
            // unlikely to happen...
            fail(e.getMessage());
        }
    }
}

Cette méthode sérialise une classe dans un buffer, puis relit ce buffer et s’assure que l’objet lu est bien égal à l’objet sérialisé.

Si ce test échoue, alors c’est que certaines données membres de l’objet testé ne sont pas sérialisables. Par exemple, un Logger ou un Thread ne peuvent pas être sérialisés. Des données membres de ce type doivent donc être déclarés transient de la manière suivante

class Foo {
    transient private Thread thread;
    transient private Logger logger = LoggerFactory.getLogger(this.getClass());
    private String name;
 
    // etc...
}

Attention une fois l’objet sérialisé puis désérialisé, les membres transients sont perdus. C’est ainsi qu’on se retrouve avec un logger à null et un NullPointerException assez rapidement… Dans ce cas, on peut implémenter une méthode privée ayant la signature readObject(ObjectInputStream) pour y restaurer le logger. La méthode doit être privée pour éviter tout appel par une autre classe ou toute surcharge par une classe fille.

    // code...
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        // default implementation
        in.defaultReadObject();
        // restore our logger object
        logger = LoggerFactory.getLogger(this.getClass());
    }
 
    // code...

Tout les détails de la sérialisation sont expliqués sur le site de Sun.

Trackback

aucun commentaire

Ajouter un commentaire