(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~
Java SE 096 对象序列化与反序列化深入详解
文章目录
1.序列化
1.1对象序列化
将对象转换为字节流保存起来,并在以后还原这个对象,这种机制叫做对象序列化。
1.2持久化
将一个对象保存到永久存储设备上称为持久化。
1.3 实现序列化
一个对象要想能够实现序列化,必须实现Serializable接口或Externalizable接口
(1)当编译器看到某一个类实现Serializable接口了,它就知道这个类是可以序列化的。
(2)即,一个类如果想被序列化,则需要实现java.io.Serializable接口,该接口中没有定义任何方法,是一个标识性接口(Marker Interface),当一个类实现了该接口,就表示这个类的对象是可以序列化的。
(3)序列化(serialization)是把一个对象的状态写入一个字节流的过程。
(4)当你想要把你的程序状态存入一个固定的存储区域例如文件时,它是很管用的。稍后一点时间,你就可以运用序列化过程存储这些对象。
(5)假设一个被序列化的对象引用了其他对象,同样,其他对象又引用了更多的对象。这一系列的对象和它们的关系形成了一个顺序图表。在这个对象图表中也有循环引用。也就是说,对象X可以含有一个对象Y的引用,对象Y同样可以包含一个对象X的引用。对象同样可以包含它们自己的引用。对象序列化与反序列化的工具被被设计出来并在这一假定条件下运行良好。如果你试图序列化一个对象图表中顶层的对象,所有的其他的引用对象都被循环的定位和序列化。同样,在反序列化过程中,所有的这些对象以及它们的引用都被正确的恢复。
1.4序列化的一些特点
(1)当一个对象被序列化时,只保存对象的非静态成员变量 ,不能促存任何的成员方法和静态的成员变量。
解说:因为静态成员是属于类本身的,而不是属于对象的,所以不可以序列化,
(2)如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。
(3)如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。我们可将这个引用标记为transient(瞬时),那么对象仍然可以序列化。
解说:如果将一个成员变量标识为transient,则在序列化时,该变量就不会被序列化。
1.5小结
(1)在序列化时,static变量是无法序列化的;如果A包含了B的引用,那么在序列化A时也会将B序列化,如果此时A可以序列化,B无法序列化,那么当序列化A时,就会抛出异常,这时就需要对B的引用设为transient,该关键字表示变量不会被序列化。
1.5Serializable接口
(1)只有实现Serializable接口的对象可以被序列化工具存储和恢复。
(2)Serializable接口没有定义任何成员。它只用来表示一个类可以被序列化。如果一个类可以被序列化,它的所有子类都可以序列化。
(3)声明成transient的变量不被序列化工具存储。同样,static变量也不被存储。
2.序列化对象实战
2.1定义需要序列化的类
package com.javareview.serializable;
import java.io.Serializable;
/**
* 定义需要被序列化的类
*/
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private int age;
/**
* transient用于指定成员变量不以字节流的方式保存到文件
*/
private transient String name;
private double height;
public Person(int age,String name, double height){
this.age = age;
this.name = name;
this.height = height;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
2.2将对象通过序列化的方式,以字节流的形式保存到文件中
package com.javareview.serializable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* 序列化对象:将对象以字节流的形式存储到文件中
*/
public class SerizalizableTest {
public static void main(String[] args) throws IOException {
//s1.创建需要被序列化的对象
Person p1 = new Person(23,"Xiongjie",1.41);
Person p2 = new Person(25,"Liu Manlin",1.50);
//s2.将需要序列化的对象通过io流的方式写入文件,即将对象以字节流的形式保存到文件中
FileOutputStream fos = new FileOutputStream("person.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p1);
oos.writeObject(p2);
oos.close();
}
}
3.反序列化
使用反序列化的机制从持久化存储里面将对象的信息加载到内存。
package com.javareview.serializable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* 反序列化:将对象从文件中反序列化到内存中
*/
public class SerizalizableTest1 {
public static void main(String[] args) throws ClassNotFoundException, IOException {
//s1.将对象通过字节流的形式由文件加载到内存的过程称为反序列化
FileInputStream fis = new FileInputStream("person.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Person p = null;
for(int i = 0 ; i < 2 ; i++){
p = (Person) ois.readObject();
System.out.println(p);
}
ois.close();
}
}
4.在序列化与反序列化过程中需要特殊处理的方法
(1)private void writeObject(java.io.ObjectOutputStream out);
(2)private void readObject(java.io.ObjectInputStream in);
package com.javareview.serializable;
import java.io.IOException;
import java.io.Serializable;
/**
* 定义需要被序列化的类
*/
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private int age;
/**
* transient用于指定成员变量不以字节流的方式保存到文件
*/
private transient String name;
private double height;
public Person(int age,String name, double height){
this.age = age;
this.name = name;
this.height = height;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public String toString() {
return "Person [age=" + age + ", height=" + height + "]";
}
private void writeObject(java.io.ObjectOutputStream out)throws IOException{
System.out.println("write object");
}
private void readObject(java.io.ObjectInputStream in)throws IOException{
System.out.println("read object");
}
}
4.1序列化过程的运行结果如下
write object
write object
4.2反序列化过程的运行结果如下
read object
Person [age=0, height=0.0]
read object
Person [age=0, height=0.0]
(1)为何在读的时候没读到对象内容呢?
就是因为我们在对对象写入的处理过程中(writeObject())没有写入具体值。如果要对对象写入操作进行特殊处理,比如说指定哪些值可以写入,哪些值不需要写入,则在对象的writeObject()方法中去实现。
package com.javareview.serializable;
import java.io.IOException;
import java.io.Serializable;
/**
* 定义需要被序列化的类
*/
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private int age;
/**
* transient用于指定成员变量不以字节流的方式保存到文件
*/
private transient String name;
private double height;
public Person(int age,String name, double height){
this.age = age;
this.name = name;
this.height = height;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public String toString() {
return "Person [age=" + age + ", height=" + height + "]";
}
private void writeObject(java.io.ObjectOutputStream out)throws IOException{
out.writeInt(age);
out.writeUTF(name);
System.out.println("writeObject");
}
private void readObject(java.io.ObjectInputStream in)throws IOException{
age = in.readInt();
name = in.readUTF();
System.out.println("readObject");
}
}
readObject
Person [age=23, height=0.0]
readObject
Person [age=25, height=0.0]
(1)在对象中提供writeObject()和readObject()方法,可以更细粒度的方式去处理序列化与反序列化过程。
(2)如果对象没有提供这两个方法,JDK ObjectInputStream, ObjectOutputStream这些类会以宏观的方式来对序列化与反序列化进行处理。
(3)当我们在一个待序列化/反序列化的类中实现了以上两个private方法(方法声明要与上面的保持完全一致,对象序列化过程中,会自动得到调用)那么就允许我们以更加底层、更加细粒度的方式控制序列化/反序列化的过程。
5.总结
(1)另一种分类方式:节点流、过滤流。
(2)过滤流是包装了节点流的。在这种情况下,使用到了装饰模式。
(3)如果使用字节流来读取数据的时候,用位的方法,可以一次读一个字节,或者是一次读到一个字节数组里面。Reader和Writer接收的是一个char类型的数组。
(4)如果是处理一个文本文件的话,最好要使用一个BufferedReader。可以一次读取一行,是否读到文件末尾,是用空去判断的。如果读取的内容为null,则读到文件末尾了,否则可以继续往下读。