Java序列化——Serializable和Externalizable
1. 继承Serializable接口
import java.io.Serializable;
/**
* @author gyh
* @csdn https://blog.csdn.net/qq_40788718
* @date 2020/8/12 22:51
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L ;
private String name ;
private Integer age ;
public User(){
}
public User(String name , Integer age){
this.name = name ;
this.age = age ;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.io.*;
/**
* @author gyh
* @csdn https://blog.csdn.net/qq_40788718
* @date 2020/8/12 22:53
*/
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("F:\\2.txt") ;
if (!file.exists()){
file.createNewFile();
}
serializeUser() ;
unserializeUser();
}
private static void unserializeUser() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\2.txt"));
User user = (User) ois.readObject();
System.out.println(user.toString());
System.out.println("反序列化成功");
ois.close();
}
private static void serializeUser() throws IOException {
User user = new User("guoyuhang" , 22) ;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\2.txt")) ;
oos.writeObject(user) ;
oos.close() ;
System.out.println("序列化成功");
}
}
序列化成功
User{name='guoyuhang', age=22}
反序列化成功
sr serializable.User;.V? L aget Ljava/lang/Integer;L namet Ljava/lang/String;xpsr java.lang.Integer鉅亣8 I valuexr java.lang.Number啲?斷? xp t guoyuhang
2. 继承Externalizable接口
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* @author gyh
* @csdn https://blog.csdn.net/qq_40788718
* @date 2020/8/12 23:03
*/
public class EUser implements Externalizable {
private static final long serialVersionUID = 2106328197756487707L ;
private String name ;
private Integer age ;
private int type ;
/**
* 保留无参构造
*/
public EUser() {
}
public EUser(String name, Integer age, int type) {
this.name = name;
this.age = age;
this.type = type;
}
/**
* 需要注意的是引用类型使用writeObject
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeObject(age);
out.writeInt(type);
}
@Override
public String toString() {
return "EUser{" +
"name='" + name + '\'' +
", age=" + age +
", type=" + type +
'}';
}
/**
* 需要注意的是保证读取的顺序和writeExternal中写入的顺序相同
*/
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = (Integer) in.readObject();
type = in.readInt() ;
}
}
序列化成功
EUser{name='guoyuhang', age=22, type=3}
反序列化成功
sr serializable.EUser;.V? xpt guoyuhangsr java.lang.Integer鉅亣8 I valuexr java.lang.Number啲?斷? xp w x
3. serialVersionUID
其目的是序列化对象版本控制,有关各版本反序列化时是否兼容。如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。如果修改较小,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。
serialVersionUID有两种显示的生成方式:
- 默认的1L,比如:private static final long serialVersionUID = 1L
- 根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;
注意:
- 静态变量不会被序列化。
- 同时transient关键字修饰的变量也不会序列化,但是反序列化是会被赋为初始值。
4. Serializable和Externalizable的区别
- 序列化内容:Externalizable自定义序列化可以控制序列化的过程和决定哪些属性不被序列化。而Serializable需要通过
Transient
关键字来实现哪些不用实例化。 - Serializable序列化时不会调用默认的构造器,而Externalizable序列化时会调用默认构造器的。但是Serializable会破坏单例模式(因为序列化可以实现深克隆)。
- 使用Externalizable时,必须按照写入时的确切顺序读取所有字段状态。否则会产生异常。