Pourquoi Java a besoin d'une interface sérialisable?

114

Nous travaillons beaucoup avec la sérialisation et devoir spécifier une balise Serializable sur chaque objet que nous utilisons est une sorte de fardeau. Surtout quand c'est une classe tierce que nous ne pouvons pas vraiment changer.

La question est la suivante: puisque Serializable est une interface vide et que Java fournit une sérialisation robuste une fois que vous l'avez ajoutée implements Serializable- pourquoi n'ont-ils pas tout rendu sérialisable et c'est tout?

Qu'est-ce que je rate?

Yoni Roit
la source
Et si vous souhaitez rendre votre propre objet sérialisable? Ou ai-je mal compris quelque chose?
Joe Phillips
J'obtiendrai toujours NotSerializableException car tous les champs de mes objets doivent être sérialisables
Yoni Roit
Tout à fait d'accord avec Pop Catalin et Dmitry "Cette astuce sérialisable est juste une autre mauvaise décision qui a été prise il y a dix ou deux ans". Peu importe à quel point la sérialisation peut être dangereuse. Et ce n'est pas vrai que puisque la déclaration est explicite, alors «vous savez que vous devez faire particulièrement attention»: tous ceux qui en ont besoin mettent d'abord des «implémentations», puis réfléchissent à l'implication en cas de problème. Cela aurait pu être plus clair s'ils nous avaient donné une interface "Unserializable" à appliquer sur des cas particuliers.
Jack

Réponses:

120

La sérialisation est semée d'embûches. La prise en charge de la sérialisation automatique de ce formulaire fait des composants internes de classe une partie de l'API publique (c'est pourquoi javadoc vous donne les formes persistantes de classes ).

Pour une persistance à long terme, la classe doit être capable de décoder ce formulaire, ce qui limite les modifications que vous pouvez apporter à la conception de classe. Cela rompt l'encapsulation.

La sérialisation peut également entraîner des problèmes de sécurité. En étant capable de sérialiser tout objet auquel elle fait référence, une classe peut accéder aux données auxquelles elle ne serait normalement pas en mesure (en analysant les données d'octet résultantes).

Il y a d'autres problèmes, tels que la forme sérialisée des classes internes qui n'est pas bien définie.

Rendre toutes les classes sérialisables aggraverait ces problèmes. Consultez la deuxième édition effective de Java , en particulier l' article 74: Implémenter judicieusement sérialisable .

McDowell
la source
2
C'est clairement la meilleure réponse, je suis déçu que la réponse choisie ne soit pas celle-ci, il semble que le posté ait choisi avec un agenda "Je suis ennuyé parce que je dois déclarer des choses sérialisables" à l'esprit. C'est un exemple de quelqu'un qui veut se libérer et ne pas tenir compte des leçons de sécurité et de conception qui ont déjà été apprises dans le passé.
gbtimmon le
@McDowell qu'entendez-vous par forme persistante de classes? J'ai suivi ce lien mais je ne comprends pas ce que tu veux dire? pouvez-vous expliquer cela s'il vous plaît?
Geek
@Geek - La forme sérialisée du type d'URL (par exemple) définit quels champs privés le type doit avoir et dans quel ordre ils doivent être déclarés.
McDowell
@McDowell Pourquoi la commande est-elle importante?
Geek
12
@McDowell Bonjour. Je suis l'affiche originale de cette question d'il y a 4 ans, et je viens de sélectionner votre réponse comme acceptée, si cela signifie quelque chose. Je pense que votre réponse est vraiment la meilleure, et j'étais probablement trop immature pour la voir de cette façon à l'époque. Correction maintenant :)
Yoni Roit
32

Je pense que les utilisateurs de Java et de .Net se sont trompés cette fois-ci, il aurait été préférable de tout rendre sérialisable par défaut et de ne marquer que les classes qui ne peuvent pas être sérialisées en toute sécurité à la place.

Par exemple dans Smalltalk (un langage créé dans les années 70), chaque objet est sérialisable par défaut. Je n'ai aucune idée pourquoi ce n'est pas le cas en Java, étant donné que la grande majorité des objets sont sûrs à sérialiser et que quelques-uns ne le sont pas.

Marquer un objet comme sérialisable (avec une interface) ne le rend pas comme par magie sérialisable, il était sérialisable depuis le début , c'est juste que maintenant vous avez exprimé quelque chose que le système aurait pu trouver par lui-même, donc je ne vois aucune bonne raison pour la sérialisation étant la façon dont elle est maintenant.

Je pense que c'était soit une mauvaise décision prise par les concepteurs, soit que la sérialisation était une réflexion après coup, soit que la plate-forme n'était jamais prête à faire la sérialisation par défaut sur tous les objets de manière sûre et cohérente.

Pop Catalin
la source
26
Vous devez faire particulièrement attention lors de la conception et de l'implémentation d'une classe pour vous assurer que les instances seront sérialisées de manière raisonnable. L'interface sérialisable signifie en fait: «En tant que programmeur, j'ai compris les conséquences de la sérialisation et permettez à la JVM de sérialiser cela»
Rolf Rander
@Rolf Rander: la plupart du temps, vous n'avez pas besoin de faire attention du tout, marquez simplement la classe sérialisable. Si la sérialisation avait été activée par défaut sur tous les objets, l'état d'esprit de chaque développeur aurait également été différent, il aurait été quelque chose de naturel de faire une classe sérialisable ...
Pop Catalin
cela aurait ajouté une autre préoccupation à la liste des bonnes conceptions de classe, comme par exemple s'assurer que vous n'avez pas de fuites de mémoire. Pourtant, une bonne conception de classe nécessite de plus en plus que vos classes soient sérialisables quand elles le peuvent.
Pop Catalin
3
@StaxMan, parce que réaliser plus tard que vous devez sérialiser une classe et que vous ne pouvez pas, peut être très coûteux. C'est l'un de ces cas où peu d'efforts supplémentaires sont payants. Cela est particulièrement vrai si vous écrivez une bibliothèque et que quelqu'un d'autre la consommera sans le code source
Pop Catalin
2
Je suis entièrement d'accord avec cette réponse. Dans de nombreuses langues, tout est sérialisable par défaut. Et en fait la même chose avec JVM, car il est facile d'utiliser Reflection pour accéder à n'importe quel membre de classe, qu'il soit privé ou non. Cette Serializableastuce n'est qu'une autre mauvaise décision qui a été prise il y a dix ou deux ans, et un ajout de plus à l'ennui lorsqu'il s'agit de pur java, comme certaines failles dans la collection et le traitement des chaînes dans la bibliothèque standard. Heureusement, il y a Kryo, mais c'est une dépendance et il faut d'abord la trouver. C'est ainsi que la sérialisation intégrée aurait dû être effectuée.
dmitry
20

Tout n'est pas vraiment sérialisable. Prenez une connexion de socket réseau, par exemple. Vous pourriez sérialiser les données / l'état de votre objet socket, mais l'essence d'une connexion active serait perdue.

Joel Coehoorn
la source
Ce serait mon problème et si je n'étais pas assez intelligent pour essayer de sérialiser le socket, je trouverais mon erreur lors du débogage. Cependant, maintenant je suis dans une situation où je ne peux pas du tout utiliser la sérialisation Java à cause d'une classe tierce qui n'implémente pas Serializable sans raison valable.
Yoni Roit
4
Il existe quelques moyens décents de gérer ce cas, comme l'écriture d'une classe wrapper Serializable qui sait lire et écrire les données clés de la classe tierce; rendre l'instance encapsulée transitoire et remplacer writeObject et readObject.
Greg Case
Pouvez-vous hériter des objets requis dans votre API et sérialiser ces classes?
Joel Coehoorn le
@Joel: C'est une bonne idée, mais toujours un hack. Je suppose que tout cela n'est qu'un autre compromis qui a mal tourné. Merci pour vos commentaires.
Yoni Roit
1
C'est l'un de ces rares exemples qui illustre que tout ce dont nous avons vraiment besoin est implements NotSerializable:)
Rob Grant
13

Le rôle principal de Serializable en Java est de rendre, par défaut, tous les autres objets non sérialisables. La sérialisation est un mécanisme très dangereux, en particulier dans son implémentation par défaut. Par conséquent, comme l'amitié en C ++, il est désactivé par défaut, même si cela coûte un peu cher de rendre les choses sérialisables.

La sérialisation ajoute des contraintes et des problèmes potentiels car la compatibilité des structures n'est pas assurée. C'est bien qu'il soit désactivé par défaut.

Je dois admettre que j'ai vu très peu de classes non triviales où la sérialisation standard fait ce que je veux. Surtout dans le cas de structures de données complexes. Ainsi, l'effort que vous consacreriez à la sérialisation de la classe éclipse correctement le coût d'ajout de l'interface.

Uri
la source
9

Pour certaines classes, en particulier celles qui représentent quelque chose de plus physique comme un fichier, un socket, un thread ou une connexion de base de données, cela n'a absolument aucun sens de sérialiser des instances. Pour beaucoup d'autres, la sérialisation peut être problématique car elle détruit les contraintes d'unicité ou vous oblige simplement à traiter des instances de différentes versions d'une classe, ce que vous ne souhaitez peut-être pas.

Sans doute, il aurait peut-être été préférable de tout rendre sérialisable par défaut et de rendre les classes non sérialisables via une interface de mot-clé ou de marqueur - mais alors, ceux qui devraient utiliser cette option n'y penseraient probablement pas. Dans l'état actuel des choses, si vous devez implémenter Serializable, une exception vous le dira.

Michael Borgwardt
la source
4

Je pense que le but était de vous assurer, en tant que programmeur, que votre objet peut être sérialisé.

Milhous
la source
1

Devoir déclarer explicitement que les instances d'une certaine classe sont sérialisables, le langage vous oblige à réfléchir si vous devez autoriser cela. Pour les objets de valeur simples, la sérialisation est triviale, mais dans les cas plus complexes, vous devez vraiment réfléchir.

En vous appuyant simplement sur le support de sérialisation standard de la JVM, vous vous exposez à toutes sortes de problèmes de version désagréables.

L'unicité, les références à des ressources «réelles», des minuteries et de nombreux autres types d'artefacts ne sont PAS des candidats à la sérialisation.

Jeroen van Bergen
la source
0

Eh bien, ma réponse est que ce n'est pas pour une bonne raison. Et d'après vos commentaires, je peux voir que vous avez déjà appris cela. D'autres langages essaient volontiers de sérialiser tout ce qui ne saute pas dans une arborescence après avoir compté jusqu'à 10. Un objet doit être sérialisable par défaut.

Donc, ce que vous devez faire est de lire vous-même toutes les propriétés de votre classe tierce. Ou, si c'est une option pour vous: décompiler, mettre le putain de mot-clé là-bas et recompiler.

nda1983
la source
2
La sérialisation ajoute des contraintes et des problèmes potentiels car la compatibilité des structures n'est pas assurée. IMHO, il est bon qu'il soit désactivé par défaut.
Uri
Je ne suis pas certain de ce que vous entendez par «compatibilité de structure».
nes1983
0

Certaines choses en Java ne peuvent tout simplement pas être sérialisées car elles sont spécifiques à l'exécution. Des éléments comme les flux, les threads, le runtime, etc. et même certaines classes GUI (qui sont connectées au système d'exploitation sous-jacent) ne peuvent pas être sérialisés.


la source
0

Bien que je sois d'accord avec les points soulevés dans d'autres réponses ici, le vrai problème est avec la désérialisation: si la définition de classe change, il y a un risque réel que la désérialisation ne fonctionne pas. Ne jamais modifier les champs existants est un engagement assez important pour l'auteur d'une bibliothèque! Le maintien de la compatibilité des API est déjà une corvée.

RideauChien
la source
Ce n'est pas correct. Vous devez lire le chapitre Gestion des versions des objets sérialisables de la spécification de sérialisation d'objets, qui n'existerait pas si ce que vous prétendez ici était vrai. La définition de classe peut changer dans des limites assez larges avant de devenir incompatible avec les sérialisations antérieures. Et ce n'est certainement pas la réponse à la question. La vraie raison a à voir avec les problèmes de sécurité.
Marquis de Lorne
@EJP, dans l'intérêt de la vérité, j'ai édité ma réponse pour refléter vos points. Le sentiment demeure cependant, c'est un gros engagement qui devrait certainement être opt-in plutôt que opt-out
CurtainDog
0

Une classe qui doit être conservée dans un fichier ou un autre support doit implémenter une interface sérialisable, de sorte que JVM puisse autoriser la sérialisation de l'objet de classe. Pourquoi la classe Object n'est pas sérialisée alors aucune des classes n'a besoin d'implémenter l'interface, après tout JVM sérialise la classe uniquement lorsque j'utilise ObjectOutputStream, ce qui signifie que le contrôle est toujours entre mes mains pour laisser la JVM se sérialiser.

La raison pour laquelle la classe Object n'est pas sérialisable par défaut dans le fait que la version de la classe est le problème majeur. Par conséquent, chaque classe intéressée par la sérialisation doit être marquée comme sérialisable explicitement et fournir un numéro de version serialVersionUID.

Si serialVersionUID n'est pas fourni, nous obtenons des résultats inattendus lors de la désérialisation de l'objet, c'est pourquoi JVM lève InvalidClassException si serialVersionUID ne correspond pas. Par conséquent, chaque classe doit implémenter une interface Serializable et fournir serialVersionUID pour s'assurer que la classe présentée aux deux extrémités est identique.

Trinesh Andhurthi
la source