Question

En java, si une classe implémente Serializable mais est abstraite, devrait-il avoir un serialVersionUID longtemps déclaré, ou faire les sous-classes exigent seulement?

Dans ce cas, il est en effet l'intention que toutes les sous-classes traitent sérialisation comme le but du type doit être utilisé dans les appels RMI.

Était-ce utile?

La solution

Le serialVersionUID est prévu pour déterminer la compatibilité entre un objet deseralized et la version actuelle de la classe. En tant que tel, il est pas vraiment nécessaire dans la première version d'une classe, ou dans ce cas, dans une classe de base abstraite. Vous aurez jamais une instance de cette classe abstraite à linéariser / désérialiser, donc il n'a pas besoin de serialVersionUID.

(Bien sûr, il ne génère un avertissement du compilateur, que vous voulez vous débarrasser de, non?)

Il se trouve le commentaire de James est correct. Le serialVersionUID d'une classe de base abstraite ne se propager aux sous-classes. À la lumière de cela, vous ne besoin de la serialVersionUID dans votre classe de base.

Le code à tester:

import java.io.Serializable;

public abstract class Base implements Serializable {

    private int x = 0;
    private int y = 0;

    private static final long serialVersionUID = 1L;

    public String toString()
    {
        return "Base X: " + x + ", Base Y: " + y;
    }
}



import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Sub extends Base {

    private int z = 0;

    private static final long serialVersionUID = 1000L;

    public String toString()
    {
        return super.toString() + ", Sub Z: " + z;
    }

    public static void main(String[] args)
    {
        Sub s1 = new Sub();
        System.out.println( s1.toString() );

        // Serialize the object and save it to a file
        try {
            FileOutputStream fout = new FileOutputStream("object.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fout);
            oos.writeObject( s1 );
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Sub s2 = null;
        // Load the file and deserialize the object
        try {
            FileInputStream fin = new FileInputStream("object.dat");
            ObjectInputStream ois = new ObjectInputStream(fin);
            s2 = (Sub) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println( s2.toString() );
    }
}

Exécuter le principal sous une fois pour l'obtenir pour créer et enregistrer un objet. Puis changez la serialVersionUID dans la classe de base, commentez les lignes principales qui économisent l'objet (il ne sauve pas encore, vous voulez juste charger l'ancien), et l'exécuter à nouveau. Cela se traduira par une exception

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

Autres conseils

Oui, en général, pour la même raison que toute autre classe a besoin d'un identifiant de série - pour éviter un être généré. Fondamentalement, toute catégorie (pas d'interface) qui implémente sérialisable doit définir id version de série ou vous risquez des erreurs désérialisation lorsque la même compilation .class est pas dans les JVMs serveur et client.

Il y a d'autres options si vous essayez de faire quelque chose de fantaisie. Je ne sais pas ce que vous entendez par « il est dans l'intention des sous-classes ... ». Est-ce que vous allez écrire des méthodes de sérialisation personnalisées (par exemple. WriteObject, readObject)? Dans ce cas, il existe d'autres options pour faire face à une super-classe.

voir: http://java.sun.com/javase/6 /docs/api/java/io/Serializable.html

HTH Tom

En fait, pointant sur le lien de Tom si elle manque serialVersionID est effectivement calculé par sérialisation d'exécution lors de la compilation à savoir pas

  

Si une classe sérialisable ne déclare pas explicitement   serialVersionUID, l'environnement d'exécution de sérialisation calcule un   valeur par défaut serialVersionUID pour cette classe en fonction de divers aspects   de la classe ...

Cela rend les choses encore plus compliquées ayant différentes versions de JRE.

Conceptuellement, les données sérialisés ressemblent à ceci:

subClassData(className + version + fieldNames + fieldValues)
parentClassData(className + version + fieldNames + fieldValues)
... (up to the first parent, that implements Serializable)

Ainsi, lorsque vous désérialiser, dans la version non-concordance dans l'une des classes dans la hiérarchie provoque la désérialisation à l'échec. Rien n'est stocké pour les interfaces, il n'y a donc pas besoin de spécifier la version pour eux.

Réponse: oui, vous avez besoin de fournir serialVersionUID dans la classe abstraite de base aussi bien. Même si elle ne possède pas de champs (className + version sont stockés même s'il n'y a pas de champs).

A noter également les éléments suivants:

  1. Si la classe ne dispose pas d'un champ, qui se trouve dans les données sérialisés (un champ supprimé), il est ignoré.
  2. Si la classe a un champ, qui ne figure pas dans les données sérialisés (un nouveau champ), il est réglé sur 0 / faux / null (non à la valeur par défaut comme on pouvait s'y attendre).
  3. si un champ change type de données, la valeur désérialisée doit être cessible au nouveau type. Par exemple. si vous aviez un champ Object avec une valeur String, en changeant le type de champ à String réussira, mais en changeant de Integer ne sera pas. Cependant, le changement de champ int à long ne fonctionnera pas, même si vous pouvez attribuer une valeur de int à long variable.
  4. Si la sous-classe ne prolonge plus la classe parente, qu'elle prolonge dans les données sérialisés, il est ignoré (comme dans le cas 1).
  5. Si une sous-classe étend maintenant une classe, qui ne se trouve pas dans les données sérialisés, les champs de classe parente sont restaurés avec 0 / valeur false / null (comme dans le cas 2).

En mots simples: vous pouvez modifier l'ordre des champs, ajouter et les supprimer, changer même la hiérarchie des classes. Vous ne devriez pas renommer des champs ou des classes (il ne manquera pas, mais les valeurs ne seront pas désérialisée). Vous ne pouvez pas modifier le type de champs de type primitif, et vous pouvez modifier les champs de type de référence à condition que le nouveau type est cessible de toutes les valeurs.

. Remarque: si la classe de base ne met pas en œuvre Serializable et seule la sous-classe ne puis champs de la classe de base se comporterait comme transient

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top