序列化是什么?

一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化。
换句话说,序列化只是表示对象的一种方式而已。

​序列化​​:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的

​反序列化​​:将字节数组重新构造成对象
序列化对象写入文件后,可以从文件中读取并反序列化。

整个过程独立JVM,意味着一个对象可以在一个平台上序列化,并在完全不同的平台上反序列化。

类实现序列化需要满足两个条件

  • 类必须实现​​java.io.serializable​​接口
  • 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为​​transient​

如果你想知道Java标准类是否可序列化,请检查类的文档。测试很简单,如果类实现​​java.io.serializable​​接口,那么它是可序列化的;否则,它不是。

序列化对象

​ObjectOutputStream​​类用于序列化一个对象

这里用​​Person​​​对象来演示:
Person实体类:

import java.io.Serializable;

public class Person implements Serializable{

private static final long serialVersionUID = 6815682549858613204L;


private long id;
private String name;
private transient int age;
private String gender;

public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}

public Person(long id, String name, int age, String gender) {
super();
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}

public Person() {
super();
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age
+ ", gender=" + gender + "]";
}
}

序列化测试:
将对象序列化存储到文件中

try {
Person p = new Person(1231L, "李如花", 18, "woman");
FileOutputStream fos = new FileOutputStream("/hello.tmp");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}

反序列化对象

​ObjectInputStream​​类用于序列化一个对象,将对象从文件中反序列化出。

Ps: 当然这个地方需要注意类型转换。

为了演示方便,我们就对上文中已经被序列化的对象反序列化

反序列化对象测试:

try {
FileInputStream fis = new FileInputStream("/hello.tmp");
ObjectInputStream ois = new ObjectInputStream(fis);
Person person = (Person)ois.readObject();
System.out.println(person.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e){
e.printStackTrace();
}

​Console控制台:​

这是你可能会有疑惑,为什么​​李如花​​​的​​age​​​是0,而不是18。这个时候就要说说​​transient​​的作用了。

transient是什么?

被声明为​​transient​​​的属性不会被序列化,所以上文中​​person​​​对象在序列化的时候就没有存储​​age​​这个属性,所以在反序列化时age属性值为默认初始值0。

serialVersionUID是什么?

Java序列化机制是通过在运行时判断类的​​serialVersionUID​​​来验证版本一致性的。
在进行反序列化,Java虚拟机会把传过来的字节流中的​​​servialVersionUID​​​和本地相应实体类的​​servialVersionUID​​​进行比较,
如果相同就认为是一致的实体类,可以进行反序列化,否则JVM会拒绝对这个实体类进行反序列化并抛出异常。

serialVersionUID的生成方式
  • 默认的1L
  • 根据类名、接口名、成员方法以及属性等来生成一个64位的Hash字段

如果实现 ​​java.io.Serializable​​​接口的实体类没有显式定义一个名为​​serialVersionUID​​​、类型为long的变量时,Java序列化 机制会根据编译的.class文件自动生成一个​​serialVersionUID​​​,如果​​.class​​​文件没有变化,那么就算编译再多 次,​​serialVersionUID​​也不会变化。

序列化会保存哪些信息

  • 序列化保存的是对象的状态
  • 不会保存静态属性
  • 不会保存对象的方法

什么样的类可以被序列化

要成功序列化类,必须满足两个条件:

  • 类必须实现​​java.io.serializable​​接口
  • 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为​​transient​

父类序列化情况

  • 当父类实现了​​Serializable​​接口时,所有子类都可以被序列化。
  • 当一个对象的实例变量引用其他对象时,序列化该对象时也会把引用对象进行序列化。
  • 子类实现了​​Serializable​​接口,父类没有,父类中的属性不能序列化(不报错,数据丢失),但是在子类中属性仍能正确序列化。
  • 反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错
  • 反序列化时,如果serialVersionUID被修改,则反序列化时会失败

参考文章: