Sérialisation
Java fournit un mécanisme de sérialisation d'objets. Dans ce mécanisme, un objet peut être représenté sous forme de séquence d'octets. La séquence d'octets comprend les données de l'objet, les informations sur le type de l'objet et les données stockées dans l'objet. Types de.
Avantages des données de stockage sérialisées
Le fichier sérialisé facilite la restauration du programme à son état d'origine et le fichier sérialisé est plus sûr car il ne s'agit pas du contenu d'origine, mais a été codé. Les objets sérialisés sont faciles à transférer entre les JVM. Ces JVM peuvent être sur le même ordinateur ou à distance.
Quand utiliser la sérialisation
Si l'objet doit être transmis en ligne ou enregistré sur disque pour pouvoir y accéder ultérieurement, il peut être sérialisé dans ce cas. Si l'état interne de l'objet n'est pas adapté à la sortie vers un périphérique de stockage secondaire (disque dur) ou à la transmission sur le réseau, la sérialisation doit être utilisée avec précaution. La sérialisation d'objets est utilisée pour prendre en charge deux fonctionnalités: l'appel de méthode à distance Java et Java Bean.
Implémentation de la sérialisation en Java
Les classes qui implémentent la sérialisation et la désérialisation en Java sont respectivement ObjectOutputStream et ObjectInputStream.
Méthode de sérialisation ObjectOutputStream:
public final void writeObject(Object x) throws IOException
Méthode de désérialisation ObjectInputStream:
public final Object readObject() throws IOException,ClassNotFoundException
Cette méthode prend l'objet suivant du flux et désérialise l'objet. Sa valeur de retour est Object, vous devez donc le convertir en un type de données approprié.
Exemple de code:
Classe d'employé
package JavaReview.FileIO;
import java.io.Serializable;
public class Employee implements Serializable {
public String name;
public String address;
public transient int SSN;
public int number;
public Task ta;
public Employee(String name, String address, int SSN, int number, Task ta) {
this.name = name;
this.address = address;
this.SSN = SSN;
this.number = number;
this.ta = new Task(ta);
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", SSN=" + SSN +
", number=" + number +
", ta=" + ta.toString() +
'}';
}
}
class Task implements Serializable{
public String taskName;
public String taskContent;
public Task(Task ta) {
this.taskName = ta.taskName;
this.taskContent = ta.taskContent;
}
public Task(String taskName,String taskContent ){
this.taskContent=taskContent;
this.taskName=taskName;
}
@Override
public String toString() {
return "Task{" +
"taskName='" + taskName + '\'' +
", taskContent='" + taskContent + '\'' +
'}';
}
}
Réaliser la sérialisation et la désérialisation
package JavaReview.FileIO;
import java.io.*;
public class SerializeDemo {
public static void main(String[] args) {
Task ta=new Task("项目XXX","完成XXX");
Employee ee=new Employee("张三","中国北京市",12312,111,ta);
Employee ee2=new Employee("李四","中国上海市",122222,222,ta);
try {
//开始序列化对象ee,ee2
FileOutputStream outfileStream=new FileOutputStream("example.ser");
ObjectOutputStream os=new ObjectOutputStream(outfileStream);
os.writeObject(ee);
os.writeObject(ee2);
os.close();
outfileStream.close();
System.out.println("写入文件成功");
//开始反序列化对象ee,ee2
FileInputStream infileStream=new FileInputStream("example.ser");
ObjectInputStream is=new ObjectInputStream(infileStream);
Employee e=null;
Employee e2=null;
e=(Employee) is.readObject();
e2=(Employee) is.readObject();
System.out.println(e.toString());
System.out.println(e2.toString());
infileStream.close();
is.close();
}catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
résultat de l'opération
写入文件成功
Employee{name='张三', address='中国北京市', SSN=0, number=111, ta=Task{taskName='项目XXX', taskContent='完成XXX'}}
Employee{name='李四', address='中国上海市', SSN=0, number=222, ta=Task{taskName='项目XXX', taskContent='完成XXX'}}
Pour qu'un objet d'une classe soit sérialisé avec succès, deux conditions doivent être remplies:
- Cette classe doit implémenter l'objet java.io.Serializable. Serializable est une interface et il n'y a aucune méthode à implémenter. Sa seule fonction est de déclarer que la classe qui l'implémente peut être sérialisée.
- Tous les attributs de cette classe doivent être sérialisables. S'il existe un attribut qui n'est pas sérialisable, l'attribut doit être marqué comme transitoire avec le mot-clé transitoire.
Dans l'exemple ci-dessus, lorsque l'objet est sérialisé, la valeur de l'attribut SSN est 111222333, mais comme l'attribut est court, la valeur n'est pas envoyée au flux de sortie. Par conséquent, l'attribut SSN de l'objet Employee est 0 après la désérialisation. En même temps, il y a une référence d'objet ta dans l'objet sérialisé, et la classe ta est également hérite de l'interface Serializable, de sorte que la variable ta elle-même et le contenu de l'objet pointé par ta peuvent et seront sérialisés. Si l'héritage d'interface de Task est supprimé, une erreur de non-héritage de l'interface sera signalée. comme suit:
java.io.NotSerializableException: JavaReview.FileIO.Task
Ce qui suit explique spécifiquement quelques points:
-
La sérialisation et la désérialisation doivent être placées dans try / catch pour l'opération. La désérialisation doit prendre en compte le cas où le fichier lu n'existe pas, il est donc nécessaire d'ajouter l'exception ClassNotFoundException.
-
FileOutputStream / FileInputStream peut écrire / lire des octets dans des fichiers, mais nous n'écrivons généralement pas d'octets directement, mais nous écrivons d'un point de vue au niveau de l'objet. Par conséquent, un flux de connexion de haut niveau est requis. Les classes ObjectInputStream
et ObjectOutputStream sont élevées Un flux de données hiérarchique peut transformer des objets en flux d'octets. -
Certaines variables ne peuvent pas être sérialisées. Par exemple, ces données sont dynamiques, c'est-à-dire qu'elles ne sont trouvées que pendant l'exécution et ne peuvent pas ou n'ont pas besoin d'être stockées. Elles peuvent être créées sur place pendant l'exécution pour avoir un sens. Une fois le programme fermé, la connexion elle-même est non seulement utilisé, et doit être recréé la prochaine fois qu'il est exécuté.
-
Si l'objet a une classe ancêtre non sérialisable dans l'arborescence d'héritage, le constructeur de la classe non sérialisable et la classe au-dessus (même si elle est sérialisable) seront exécutés. Une fois que la chaîne de constructeurs démarre, elle ne peut pas être arrêtée, c'est-à-dire qu'à partir de la première classe parente non sérialisable, elles seront toutes réinitialisées.
-
La variable transitoire se verra attribuer une référence d'objet nulle ou le type de données primaire primitif par défaut est 0, faux, etc.
-
Lors de la désérialisation, le nouvel objet sera configuré sur le tas, mais le constructeur ne sera pas exécuté. Parce que c'est pour revenir à l'état où il a été stocké, si le constructeur est exécuté, il devient un état complètement nouveau.
-
Les variables statiques ne seront pas sérialisées, car static est une variable de classe, pas une variable d'objet, c'est-à-dire que le stockage de l'objet n'affectera pas sa valeur.
-
Lors de la désérialisation, l'ordre de lecture des objets est le même que l'ordre d'écriture.
-
Une limitation de la sérialisation d'objets est qu'elle ne peut résoudre que des solutions Java, et seuls les programmes Java peuvent désérialiser cet objet. Une solution plus interopérable consiste à convertir les données au format XML afin qu'elles puissent être utilisées par une variété de plates-formes et de langages.
Optimisation de la sérialisation
Si vous avez besoin de lire et d'écrire un grand nombre d'objets de classe spécifiques, vous devriez envisager d'utiliser l'interface E.
La seule caractéristique de la classe d'instance Externalizable est qu'elle peut être écrite dans le flux de sérialisation. Cette classe est responsable de l'enregistrement et de la restauration du contenu de l'instance. Si quelqu'un veut contrôler entièrement le format et le contenu du flux d'un objet et son supertype, il doit implémenter les méthodes writeExternal et readExternal de l'interface Externalizable. Ces méthodes doivent se coordonner explicitement avec le supertype pour enregistrer son état. Ces méthodes remplaceront les méthodes personnalisées writeObject et readObject.
Les objets de sérialisation utiliseront des interfaces sérialisables et externalisables. Les mécanismes de persistance des objets peuvent également les utiliser. Chaque objet à stocker doit être testé pour savoir s'il prend en charge l'interface externalisable. Si l'objet prend en charge Externalizable, appelez la méthode writeExternal. Si l'objet ne prend pas en charge Externalizable mais implémente Serializable, utilisez ObjectOutputStream pour enregistrer l'objet.
Conditions requises pour l'utilisation de l'interface externalisable:
- Doit implémenter cette interface
- La méthode writeExternal doit être implémentée afin de sauvegarder l'état de l'objet. Et il doit collaborer explicitement avec sa classe supérieure pour sauver son état.
- La méthode readExternal doit être implémentée pour lire les données écrites par la méthode writeExternal à partir du flux et répondre à l'état de l'objet. Et il doit collaborer explicitement avec sa classe supérieure pour sauver son état.
Matériel de référence: http://www.runoob.com/java/java-serialization.html
Matériel de référence: "Head First Java"