序列化:将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将其当前状态写入到临时或者持久性的存储区。
反序列化:通过从存储区读取或者反序列化对象,重新创建该对象。
1:对象如何序列化
首先创建一个序列化的类
class Person implements Serializable {
private String name;
private Integer age;
/**
* 1表示男 2表示女
*/
private Integer sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", sex=" + sex +
'}';
}
}
然后创建一个序列化的方法,如下:
public static void serialize() throws IOException {
Person person = new Person();
person.setName("nick");
person.setAge(22);
person.setSex(1);
ObjectOutputStream objectOutputStream =
new ObjectOutputStream(new FileOutputStream(new File("person.txt")));
objectOutputStream.writeObject(person);
objectOutputStream.close();
System.out.println("序列化完成!");
System.out.println("==============================================");
}
执行后的结果如下:
序列化完成!
==============================================
然后再反序列化,执行以下的反序列化方法
public static void deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream =
new ObjectInputStream(new FileInputStream(new File("person.txt")));
Person person = (Person) objectInputStream.readObject();
objectInputStream.close();
System.out.println("反序列化结果如下:");
System.out.println(person);
}
反序列化得到的结果如下:
反序列化结果如下:
Person{name='nick', age='22', sex=1}
可以看到通过上面的方法能够进行正确的序列化和反序列化。
2:Serializable接口有何用?
上面在定义Person类时,实现了一个Serializable接口,然而当点进Serializable接口内部查看,发现它是一个空接口!
* If a serializable class does not explicitly declare a serialVersionUID, then
* the serialization runtime will calculate a default serialVersionUID value
* for that class based on various aspects of the class, as described in the
* Java(TM) Object Serialization Specification. However, it is <em>strongly
* recommended</em> that all serializable classes explicitly declare
* serialVersionUID values, since the default serialVersionUID computation is
* highly sensitive to class details that may vary depending on compiler
* implementations, and can thus result in unexpected
* <code>InvalidClassException</code>s during deserialization. Therefore, to
* guarantee a consistent serialVersionUID value across different java compiler
* implementations, a serializable class must declare an explicit
* serialVersionUID value. It is also strongly advised that explicit
* serialVersionUID declarations use the <code>private</code> modifier where
* possible, since such declarations apply only to the immediately declaring
* class--serialVersionUID fields are not useful as inherited members. Array
* classes cannot declare an explicit serialVersionUID, so they always have
* the default computed value, but the requirement for matching
* serialVersionUID values is waived for array classes.
*
* @author unascribed
* @see java.io.ObjectOutputStream
* @see java.io.ObjectInputStream
* @see java.io.ObjectOutput
* @see java.io.ObjectInput
* @see java.io.Externalizable
* @since JDK1.1
*/
public interface Serializable {
}
但是如果不实现Serializable接口,又会报错。
Exception in thread "main" java.io.NotSerializableException: com.serial.Person
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.serial.Serialize.serialize(Serialize.java:29)
at com.serial.Serialize.main(Serialize.java:18)
那为啥会报这个错呢?
通过跟踪代码可以看到如下代码
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
从上面的代码可以看到假如一个对象既不是字符串、数组、枚举,同时也没有实现Serializable
接口的话,在序列化的时候就会抛出NotSerializableException
异常!
3:serialVersionUID的作用
经常可以看到如下的定义
private static final long serialVersionUID = -4392638638228609589L;
它有以下两个作用
-
serialVersionUID是是否可以序列化的唯一标识符。serialVersionUID序列化ID,可以看成是序列化和反序列化过程中的“暗号”,在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。
-
如果没有显式定义serialVersionUID,编译器会为它自动生成一个。 如果在定义一个可序列化的类时,没有人为显式地给它定义一个serialVersionUID的话,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息,则类的serialVersionUID也会跟着变化!
所以,为了serialVersionUID的确定性,凡是implements Serializable的类,最好人为显式地声明一个serialVersionUID值!
4:还有两种特殊情况
-
凡是被static修饰的字段是不会被序列化的,因为序列化保存的是对象的状态而static是类的状态,所以会忽略static静态域是理所应当的。
-
凡是被transient修饰符修饰的字段也是不会被序列化的,大家可以去了解一下transient修饰符的作用。如果在序列化某个类的对象时,就是不希望某个字段被序列化(比如密码),那这时就可以用transient修饰符来修饰该字段。
我们是一个技术分享平台,欢迎大家积极邮件投稿(请附带银行卡号和姓名)。为了激励大家的积极性,投稿发布后产生的广告收入,会全部通过转账的方式回馈给大家,大家也可以把文章分享到朋友圈,这样可以获得更多的广告收入。