Avant d'introduire la sérialisation, vous devez d'abord connaître les concepts suivants :
Non-persistance : Pour les objets qui existent dans la JVM (Java Virtual Machine), leur état interne ne peut être maintenu qu'en mémoire. cesse de fonctionner, l'état interne disparaît, il est donc non persistant.
Persistance : Si vous souhaitez enregistrer l'objet de manière permanente (c'est-à-dire persistance), l'approche habituelle consiste à l'enregistrer dans un fichier ou une base de données.
Sérialisation : Si vous souhaitez obtenir la persistance d'un objet en Java, vous devez le sérialiser Grâce à la sérialisation, vous pouvez facilement le sérialiser dans la JVM active. L'objet est converti en un tableau d'octets (flux) pour le stockage.
Désérialisation : convertissez un tableau d'octets (flux) dans un fichier ou une base de données en un objet actif JVM.
En Java, les classes peuvent être sérialisées en implémentant les interfaces Serialisable et Externalisable.
Une fois qu'une classe implémente l'interface Serialisable, cela signifie qu'elle peut être sérialisée.
Des opérations spécifiques de sérialisation/désérialisation doivent être implémentées via des flux d'objets (ObjectOutputStream/ObjectInputStream).
Regardons un exemple spécifique :
class Person implements Serializable { private String name; private int age; public Person(String name ,int age){ this.name = name; this.age =age; } public String getName() { return name; } public int getAge() { return age; } @Override public String toString() { return "name is " + name + " , age is " + age; } }public class Test { private final static String TEMPFILE = "E:" + File.separator + "test.txt"; public static void main(String[] args) { Person person = new Person("test",100); write(person); // 关键 -> 序列化之后重新对对象进行赋值 person = new Person("hello",999); read(person); // 输出结果: // name is test , age is 100,序列化成功 // 反序列化成功,name is test , age is 100 } // 通过 ObjectOutputStream 进行对象的序列化操作 private static void write(Person person) { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(TEMPFILE)); oos.writeObject(person); oos.close(); System.out.println(person+",序列化成功"); } catch (Exception e) { e.printStackTrace(); } } // 通过 ObjectInputStream 进行对象的反序列化操作 private static void read(Person person) { try { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(TEMPFILE)); person = (Person) ois.readObject(); ois.close(); System.out.println("反序列化成功.,"+person); } catch (Exception e) { e.printStackTrace(); } } }
Observons les résultats de sortie. Nous réattribuons l'objet après la sérialisation, et le résultat de la désérialisation est toujours le même qu'avant la sérialisation de l'objet. Les valeurs sont cohérentes, ce qui prouve également indirectement que l'objet est stocké de manière permanente et que la persistance est atteinte.
Échec de la sérialisation Il y a deux situations : ne pas vouloir être sérialisé ou ne pas pouvoir l'être.
Dans une classe, les variables membres modifiées par les mots-clés transient et static indiquent qu'elles ne souhaitent pas être sérialisées. Cela provoque un échec partiel de la sérialisation.
Dans une classe, s'il existe une variable membre de type Thead, la classe ne peut pas être sérialisée, et son impact est global.
Regardons ces situations :
Chaque fois qu'une variable est modifiée par celle-ci, cela signifie que Les paramètres sont transitoires et ne veulent pas être sérialisés ( non désérialisables ).
// 序列化过程、调用过程与上述例子一致,省略代码... // 这里只对内部类 Person 的 age 属性进行修改class Person implements Serializable { // 用 transient 修该变量 private transient int age; // 省略部分代码...} // 调用后的输出结果: // name is test , age is 100,序列化成功 // 反序列化成功.,name is test , age is 0
Observez le résultat et constatez que l'âge avant la sérialisation est de 100, tandis que l'âge lu lors de la désérialisation est de 0.
La valeur initiale du paramètre de type int est 0, ce qui signifie que le paramètre n'a pas été sérialisé.
Tant que les variables modifiées par celui-ci représentent des variables globales, vous pouvez accéder aux variables globales sans compter sur des objets de classe. L'opération de sérialisation consiste à sauvegarder l'objet. C'est précisément à cause de la contradiction entre cette fonctionnalité et la sérialisation que les variables globales ne sont pas sérialisées par défaut ( ne signifie pas qu'elles ne peuvent pas être sérialisées ).
// 序列化过程、调用过程与上述例子一致,省略代码... // 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable { // 用 static 修该变量 private static String name; // 省略部分代码...} // 输出结果: // name is test , age is 100,序列化成功//反序列化成功.,name is hello , age is 100
Observez les résultats de sortie et constatez que la valeur de name est bonjour, pas de test avant la sérialisation.
Les différentes valeurs avant et après indiquent qu'il n'a pas été sérialisé. Et comme les caractéristiques de static sont liées aux classes, la variable a été réaffectée après l'opération de sérialisation, ce qui fait que sa valeur n'est pas nulle, mais bonjour.
Si la variable membre de l'objet contient le type Thread, elle ne peut pas être sérialisée.
// 序列化过程、调用过程与上述例子一致,省略代码... // 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable { //新增成员变量 private Thread myThread = new Thread(); //省略部分代码...} // 输出结果(抛出异常): // Caused by: java.io.NotSerializableException: java.lang.Thread
Ce qui précède décrit la situation dans laquelle la sérialisation échoue. Que se passe-t-il si la sérialisation doit être mise en œuvre dans certains cas (par exemple, assurez-vous de le faire). implémenter la sérialisation des variables globales). Ensuite, vous devez personnaliser le processus de sérialisation/désérialisation dans la classe. Jetez un œil à l'exemple suivant :
// 序列化操作代码与上面一致... // 这里只对内部类 Person 的属性进行修改。class Person implements Serializable { private static String name; private transient int age; // 自定义序列化操作 private void writeObject(ObjectOutputStream out) throws IOException{ out.defaultWriteObject(); out.writeObject(name); out.writeInt(age); } // 自定义反序列化操作 private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject(); name = (String) in.readObject(); age = in.readInt(); } // 省略部分代码...} // 输出结果: // name is test , age is 100,序列化成功 // 反序列化成功,name is test , age is 100
La classe implémente. L'interface externalisable signifie également qu'elle peut être sérialisée, mais elle présente deux limitations :
L'interface impose l'implémentation des méthodes writeExternal et readExternal pour une sérialisation personnalisée et Processus de désérialisation.
nécessite que la classe contienne un constructeur sans paramètre .
En même temps, parce que son processus de sérialisation est défini dans une méthode publique, il n'est pas sûr.
Regardez l'exemple suivant :
// 序列化操作代码与上面一致,这里只对内部类 Person 的进行修改。 class Person implements Externalizable { private static String name; private transient int age; // 重点 ->必须有无参构造函数 public Person(){ } public Person(String name ,int age){ this.name = name; this.age =age; } public String getName() { return name; } public int getAge() { return age; } @Override public String toString() { return "name is " + name + " , age is " + age; } // 实现接口的方法 @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeInt(age); } // 实现接口的方法 @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); age = in.readInt(); } } // 输出结果: // name is test , age is 100,序列化成功 // 反序列化成功,name is test , age is 100
serialVersionUID, qui est le numéro de version sérialisé.
Son rôle est de maintenir la compatibilité des versions lors de la sérialisation, c'est-à-dire que la désérialisation conserve toujours l'unicité de l'objet lorsque la version est mise à niveau.
Il existe deux façons de générer un SerialVersionUID :
L'une est une valeur fixe : 1L
L'une est via l'informatique JVM , différentes JVM peuvent adopter différents algorithmes de calcul.
Pour la méthode de génération JVM, elle présente les caractéristiques suivantes :
Si le code de l'objet reste inchangé, le serialVersionUID généré plusieurs fois Il est également inchangé.
Lorsque la méthode est modifiée, le serialVersionUID ** reste inchangé **.
Lorsque l'attribut de l'objet est modifié, le serialVersionUID** régénéré changera**.
因此说明序列化是作用于对象属性上的。
下面通过实例来探究下 serialVersionUID 的具体作用:
首先我们对对象进行序列化操作(这里取消了反序列化的操作)
// 序列化操作代码与上面例子一致...// 这里取消了反序列化的操作public static void main(String[] args) { Person person = new Person("test",100); write(person); person = new Person("java", 200); //read(person);}
然后新增一个对象的属性,再进行反序列化操作
// 省略部分代码,与上面的代码一致...class Person implements Serializable { private String name; private age; //新增成员变量 private int phone; //省略部分代码... } }public static void main(String[] args) { Person person = new Person("test",100); //write(person); person = new Person("java", 200); read(person); }// 输出结果(抛出异常):// java.io.InvalidClassException: Person;
观察代码,在序列化对象并没有添加 serialVersionUID 的情况,在对象序列化之后如果改变了对象的属性,反序列化就会抛出异常。如果对象添加了 serialVersionUID 就不会出现这种情况,这里就不验证了。
以上就是13.Java 基础 - 序列化的内容,更多相关内容请关注PHP中文网(www.php.cn)!