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 ''' } 复制代码