6 votes

Comment fonctionne le référencement en Java

Je lis un livre Java efficace qui contient l'exemple suivant. Dans l'exemple ci-dessous l'auteur copie la référence des objets présents dans l'ObjectOutputStream par la ligne suivante

byte[] ref = {0x71, 0, 0x7e, 0, 5}; // Ref #5 

Pourquoi cette référence pointe-t-elle vers l'objet date présent dans l'ObjectOutputStream ? Qu'est-ce qui est stocké dans une référence ?

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

final class Period {
    private final Date start;
    private final Date end;

    /**
     * @param start the beginning of the period
     * @param end the end of the period; must not precede start * @throws IllegalArgumentException
     *        if start is after end
     * @throws NullPointerException if start or end is null
     */
    public Period(Date start, Date end) {
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());
        if (this.start.compareTo(this.end) > 0)
            throw new IllegalArgumentException(start + " after " + end);
    }

    public Date start() {
        return new Date(start.getTime());
    }

    public Date end() {
        return new Date(end.getTime());
    }

    public String toString() {
        return start + " - " + end;
    }
    // Remainder omitted
}

public class MutablePeriod {
    // A period instance
    public final Period period;
    // period's start field, to which we shouldn't have access
    public final Date start;
    // period's end field, to which we shouldn't have access
    public final Date end;

    public MutablePeriod() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(bos);

            // Serialize a valid Period instance
            out.writeObject(new Period(new Date(), new Date()));
            /*
             * Append rogue "previous object refs" for internal * Date fields in Period. For
             * details, see "Java Object Serialization Specification," Section 6.4.
             */
            byte[] ref = {0x71, 0, 0x7e, 0, 5}; // Ref #5 
            bos.write(ref); // The start field
            ref[4] = 4; // Ref#4
            bos.write(ref); // The end field
            // Deserialize Period and "stolen" Date references
            ObjectInputStream in =
                    new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
            period = (Period) in.readObject();
            start = (Date) in.readObject();
            end = (Date) in.readObject();

        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

3voto

Peter Lawrey Points 229686

Pourquoi cette référence pointe-t-elle vers l'objet date présent dans l'ObjectOutputStream ?

Lorsque le premier objet est écrit, un identifiant est attribué à chaque objet (et classe). Lorsque vous ajoutez les octets appropriés pour que le lecteur lise la référence #5, vous obtenez cette référence d'objet. Il se trouve que les deux objets date sont #4 et #5, mais si vous écriviez des données différentes, ils auraient un id différent.

qu'est-ce qui est stocké dans une référence ?

Le lecteur stocke l'identifiant et la référence à l'objet correspondant à cet identifiant. Une référence ne stocke rien d'autre que l'objet vers lequel elle pointe.

2voto

Heri Points 28

Ces deux "ref" streamés n'ont rien à voir avec les références en Java (BTW : le tableau d'octets a 5 octets et pas seulement 4 comme vous le mentionnez dans un commentaire). Ces dix octets sont de la magie interne dans la sortie sérialisée. Les détails exacts de ce format de flux sont décrits dans le chap. 6.4 de "Java Object Serialization Specification" (les valeurs "71 00 7e 00" apparaissent même dans l'exemple en bas).

Dans un programme java, une référence contient soit 4 soit 8 octets, selon la plate-forme (32 ou 64 bits). Il s'agit d'un pointeur dans la mémoire (gérée par java) où commencent les données de l'instance de l'objet. Mais heureusement, vous n'avez jamais à vous occuper de cette valeur en Java. Je pense que vous ne pouvez même pas y accéder.

0voto

just Bob Points 1

L'exemple dans la question, la disposition de sérialisation peut considérer comme :

#0 Period Class desc
#1 String Class 
#2 Period instance - new 
#3 Date Class desc
#4 Date instance - end (name by ascending order)
#5 Date instance - start 

Lien plus détaillé : L'algorithme de sérialisation de Java a révélé . Il existe quelques différences entre les différents niveaux de compilateurs, car la version de sérialisation peut être différente.

Ensuite, lors de la désérialisation, tous les résultats résolus ci-dessus sont stockés dans un tableau d'objets par ordre - appelé entrées (maintien de la référence pour améliorer les performances, etc). Ainsi, lire un objet à partir de "{0x71, 0, 0x7e, 0, 5}" équivaut à obtenir entries[5], c'est une référence à "start".
Quand j'ai étudié la sérialisation en Java, j'ai aussi lu le manuel "Effective Java" et écrit la démo de sérialisation. Vous pouvez sonder comme ça :

byte[] ref = {0x71, 0x0, 0x7E, 0x0, 0}; // ref #0, #1, #2... 
bos.write(ref);
ObjectInputStream ois = new ObjectInputStream(new 
                        ByteArrayInputStream(bos.toByteArray()));
period = (Period)ois.readObject();
Object obj = ois.readObject();
System.out.println(obj);
if(obj instanceof ObjectStreamClass)
    System.out.println("fields: " + Arrays.toString(((ObjectStreamClass) obj).getFields()));
else 
    System.out.println(obj.getClass());

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X