Question d'entrevue: à propos de la sérialisation Java et des singletons [fermé]

Dans une interview, l'intervieweur m'a posé la question suivante: est-il possible de sérialiser un objet singleton? J'ai dit oui, mais dans quel scénario devrions-nous sérialiser un singleton?

Et est-il possible de concevoir une classe dont l'objet ne peut pas être sérialisé?

26
demandé sur BalusC 2010-06-02 18:54:13

6 réponses

La question devrait probablement être mieux formulée comme "est-il possible d'utiliser la sérialisation et la désérialisation avec une classe singleton-pattern C d'une manière qui ne casse pas le modèle singleton?"

La réponse est fondamentalement Oui:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

public class AppState implements Serializable
{
    private static AppState s_instance = null;

    public static synchronized AppState getInstance() {
        if (s_instance == null) {
            s_instance = new AppState();
        }
        return s_instance;
    }

    private AppState() {
        // initialize
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        synchronized (AppState.class) {
            if (s_instance == null) {
                // re-initialize if needed

                s_instance = this; // only if everything succeeds
            }
        }
    }

    // this function must not be called other than by the deserialization runtime
    private Object readResolve() throws ObjectStreamException {
        assert(s_instance != null);
        return s_instance;
    }

    public static void main(String[] args) throws Throwable {
        assert(getInstance() == getInstance());

            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
            java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
            oos.writeObject(getInstance());
            oos.close();

            java.io.InputStream is = new java.io.ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(is);
            AppState s = (AppState)ois.readObject();
            assert(s == getInstance());
    }
}

Mais notez que est possible pour plusieurs instances de AppState d'exister en utilisant ce code. Cependant, un seul est référencé. Les autres sont éligibles pour le garbage collection, créé uniquement par le runtime de désérialisation, de sorte qu'ils n'existent pas pour des raisons pratiques.

Pour les réponses à vos deux autres questions (dans quel scénario devrions-nous sérialiser un singleton? Est-il possible de concevoir une classe dont l'objet ne peut pas être sérialisé?), voir la réponse de @Michael Borgwardt .

23
répondu Daniel Trebbien 2017-05-23 12:17:46

Dans quel scénario nous devrions sérialiser Singleton.

Imaginez que vous avez une application de longue durée et que vous voulez pouvoir l'arrêter et continuer plus tard au point où elle a été arrêtée (par exemple pour effectuer la maintenance matérielle). Si l'application utilise un singleton avec état, vous devez pouvoir enregistrer et restaurer l'état du sigleton, ce qui est le plus facile en le sérialisant.

Et est-il possible de concevoir une classe dont l'objet ne peut pas être sérialiser.

Très simple en effet: n'implémentez pas Serializable et faites la classe final

21
répondu Michael Borgwardt 2010-06-02 14:59:01

Est-il possible de sérialiser un objet singleton?

Cela dépend de la façon dont le singleton est implémenté. Si votre singleton est implémenté en tant que type enum avec un élément, alors c'est par défaut:

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() { ... }
}

Si votre singleton n'est pas implémenté en utilisant un type enum à un seul élément mais, disons en utilisant une méthode d'usine statique (la variante consiste à utiliser un champ final statique public):

// Singleton with static factory
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public static Elvis getInstance() { return INSTANCE; }
    public void leaveTheBuilding() { ... }
}

, Puis il ne suffit pas d'ajouter implements Serializable pour le faire sérialisable, Vous devez déclarer tous les champs d'instance transitoires (pour empêcher une attaque de sérialisation) et fournir une méthode readResolve .

Pour maintenir la garantie singleton, vous devez déclarer toutes les instances champs transitoires et fournissent un readResolve Méthode (point 77). Sinon, à chaque fois qu'un sérialisé l'instance est désérialisée, une nouvelle instance sera créée, leader, dans le cas de notre exemple, à faux Des observations d'Elvis. Pour éviter cela, ajouter cette méthode readResolve à L'Elvis classe:

// readResolve method to preserve singleton property
private Object readResolve() {
     // Return the one true Elvis and let the garbage collector
     // take care of the Elvis impersonator.
    return INSTANCE;
}

Ceci est fortement discuté dans Effective Java (qui montre également l'attaque de sérialisation):

  • Item 3: Appliquer la propriété singleton avec un constructeur privé ou un type enum
  • Item 77: par exemple control, préférez les types enum à readResolve

Dans quel scénario devrions-nous sérialiser un singleton

Par exemple pour le stockage temporaire, à court terme ou pour transport d'objets sur un réseau (avec RMI, par exemple).

Et est-il possible de concevoir une classe dont l'objet ne peut pas être sérialisé.

Comme d'autres l'ont dit, n'implémentez pas Serializable. Et même si un objet ou l'une de ses superclasses implémente Serializable, vous pouvez toujours l'empêcher d'être sérialisé en lançant un NotSerializableException de writeObject().

7
répondu Pascal Thivent 2010-06-02 17:56:41

j'ai dit oui

Pas par défaut. À côté de la mise en œuvre java.io.Serializable vous devez remplacer readObject() et writeObject() readResolve() méthodes, car vous ne pouvez pas sérialiser les champs statiques. Un singleton conserve son instance dans un champ statique.

mais dans quel scénario nous devrions sérialiser un singleton.

Aucun scénario du monde réel utile ne vient à l'esprit en fait. Un singleton ne change généralement pas d'état tout au long de sa vie ni ne contient aucun État que vous souhaitez enregistrer / restaurer. Si c'était le cas, alors c'était déjà faux d'en faire un singleton.

Deux exemples du monde réel de pattern singleton dans l'API Java SE sont java.lang.Runtime#getRuntime() et java.awt.Desktop#getDesktop(). Aucun d'entre eux n'implémente serializable. Cela n'a pas non plus de sens car ils renvoient correctement l'instance droite / désirée / attendue à chaque appel. Si vous sérialisez et désérialisez, vous pouvez vous retrouver avec plusieurs instances. Si vous passer de l'environnement en attendant, l'instance peut ne pas fonctionner du tout.

Et est-il possible de concevoir une classe dont l'objet ne peut pas être sérialisé.

Oui. Ne laissez pas la classe implémenter java.io.Serializable interface.

5
répondu BalusC 2010-06-02 16:36:08

Le problème de la sérialisation d'un singleton est que vous vous retrouvez avec deux, l'original et la désérialisé copie.

Le moyen le plus évident d'empêcher la sérialisation est simplement de ne pas implémenter serializable. Cependant, vous voudrez parfois que votre singleton implémente serializable afin qu'il puisse être référencé dans un objet sérialisé sans problèmes.

Si c'est un problème, vous avez quelques options. Le mieux est de faire du singleton un seul membre enum, si possible. de cette façon, le l'implémentation Java sous-jacente prend en charge tous les détails.

Si cela n'est pas possible, vous devez implémenter les méthodes readobject et writeObject appropriées pour vous assurer que la sérialisation n'effectue pas de copie séparée.

4
répondu Yishai 2010-06-02 15:20:37

Une classe sérialisable peut être instanciée en la désérialisant, permettant plusieurs instances, ce qui en fait un singleton.

À votre deuxième question, à partir du document java pour java. Io. Serializable

La sérialisation d'une classe est activée par la classe mettant en œuvre le interface java.io. Serializable.

Donc, pour implémenter une classe qui n'est pas sérialisable, n'implémentez pas Serializable.

1
répondu Angelo Genovese 2010-06-02 15:01:48