Serializable 是一种用来处理对象流的机制。对象流就是将运行时对象的状态进行流化,即转换成二进制。说白了,就是将正在运行中程序的对象的状态/内容转换成二进制进行传输,持久化的操作。
在 Java 中,序列化是在 jdk 1.1 时引入的特性。Serializable 的定义
public interface Serializable {
}
复制代码
只有这么个接口,一旦某类实现了该接口,那么该类就有了序列化的能力,但严格来说,不一定能序列化/反序列化成功,为撒呐?先来看个 demo
举个栗子:chestnut:,有一 Women 类,该类有三个属性,分别是 age , weight 和 name ,我们来试试怎么序列/反序列化。
//Women.class
public class Women implements Serializable {
private static final long serialVersionUID = 1L;
private Integer age;
private String name;
private Float weight;
public Women(String name, int age, float weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
'''
setter/getter
'''
}
复制代码
public static void main(String[] args) throws IOException {
Women women = new Women("xiao ju",18, 45.1f);
File file = new File("xiaoju.txt");
FileOutputStream output = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(output);
objectOutputStream.writeObject(women);
output.close();
objectOutputStream.close();
}
复制代码
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("xiaoju.txt");
FileInputStream input= new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(input);
Women women = (Women) objectInputStream.readObject();
input.close();
objectInputStream.close();
}
复制代码
这就是序列化/反序列化的过程。可以看到我们在 Woemen 类中,不仅实现了 Serializable 接口外,还加了一个 serialVersionUID ,当然了,如果我们不手动加,那么系统也会根据当前类的结构会初始化一个 UID ,它的作用是什么呢?
当进行序列化的时候, UID 会和当前对象的状态一同持续久化,在反序列化的时候,会检测当前类的 UID 是否和持久化的 UID 一致,如果一致,那么反序列化成功,否则失败。
如果我们没有指定 UID 的化,系统在初始化一个 UID 后,随着对象的持久化,如果我们这个时候改了该类的数据结构,那么这个类的 UID 就会发生改变(系统初始化的 UID 是根据类的数据结构算出来的),所以在反序列化的时候,就会检测到 UID 不一致,那么就会失败。所以,一般我们都会加上一个 UID 。
确实不好,不要你觉得,要我觉得,不是所有的类都想要序列化的,有些类是比较敏感的,比如用户类,卡号类... 对于序列化的对象的信息是很容易被破解的,不能保证其安全性。
两种方式:
static 修饰符。前面我们说到,序列化/反序列化的都是程序正在运行中的对象的状态,而被 static 修饰的属性/方法其实是属于类的状态,不属于处于内存中的对象。 transient 修饰, transient 不能修饰方法/类。一旦属性被 transient 修饰,那么该属性就不会被序列/反序列化。 类成员
为了减少存储,传输空间,从提高效率,那么在进行序列化的时候,可以将不需要序列化的属性通过 static 和 transient 关键字修饰排除掉。
继承关系
Serializable Serializable
引用关系
如果一个类实现了序列化,并且引用了一个对象。该对象所属类实现了 Serializable 接口,那么会改对象也会被序列化,否则会抛出 java.io.NotSerializableExeception 。
前面我们说到,实现了 Serializable 接口的类,可以将整个或部分对象的状态进行序列化/反序列化。那么如果我们想要更加定制化的序列化/反序列化一些东西的话,那么我们就需要用到 Externalizable ,比如:在序列化的时候我想保存一个时间,反序列化的时候我希望获取到这个时间,但是这个时间我不想放到类中。
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
复制代码
可以看到 Externalizable 除了实现了 Serializable 接口外,还新增了两个方法,这两个方法就是可以在序列化和反序列化的时候做一些定制的东西。还是拿 Women 来看。
//Women.class
public class Women implements Externalizable {
private static final long serialVersionUID = 1L;
private Integer age;
private String name;
private Float weight;
public Women(String name, int age, float weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(new Date());
out.writeObject(this.name);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println(in.readObject());
this.name = (String) in.readObject();
}
'''
setter/getter
'''
}
复制代码