一直说序列化,但是为什么要实现序列化?它都实现了什么东西?在实际中的应用是什么?为什么实现接口也可以不定义序列码serialVersionUID的默认值?不定义序列码会出现什么问题?
1.序列化的定义是什么?
Serializable的介绍如下
/**
* Serializability of a class is enabled by the class implementing the
* java.io.Serializable interface. Classes that do not implement this
* interface will not have any of their state serialized or
* deserialized. All subtypes of a serializable class are themselves
* serializable. The serialization interface has no methods or fields
* and serves only to identify the semantics of being serializable. <p></span>
public interface Serializable类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
<span style="font-size:18px;"> * To allow subtypes of non-serializable classes to be serialized, the
* subtype may assume responsibility for saving and restoring the
* state of the supertype's public, protected, and (if accessible)
* package fields. The subtype may assume this responsibility only if
* the class it extends has an accessible no-arg constructor to
* initialize the class's state. It is an error to declare a class
* Serializable if this is not the case. The error will be detected at
* runtime. <p></span>
要允许不可序列化类的子类型序列化,可以假定该子类型负责保存和还原超类型的公用 (public)、受保护的 (protected) 和(如果可访问)包 (package) 字段的状态。仅在子类型扩展的类(父类)有一个可访问的无参数构造方法来初始化该类的状态时,才可以假定子类型有此责任。如果不是这种情况,则声明一个类为可序列化类是错误的。该错误将在运行时检测到。
在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数的构造方法。可序列化子类的字段将从该流中还原。
如果一个序列化的类没有显式声明serialVersionUID,然后序列化运行时将默认serialVersionUID值,但是ava对象序列化规范(TM)“强烈”推荐所有序列化的类声serialVersionUID值,因为默认serialVersionUID计算高度敏感的类的细节,这取决于编译器实现,可能会有所不同,并且在反序列化可能会导致意外的invalidclassexception 。这里稍后我们会测试在真正生产上的影响
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
。
。
。
a.Test1序列化与反序列化
分两次执行main方法输出结果如下:
b.注释掉serialVersion分别执行main方法
c.在b的基础上,再添加一个类属性然后分别执行main方法
即首次执行 start()方法,User类属性为
执行end()方法,User类属性为
end执行后,输出结果如下:
由此我们知道,当在线业务远程传出对象存在升级变更的时候,会存在对象无法反序列化
d.相反我们添加上序列编码会发现 一切正常
由此得出结论,还是能加就加上吧
友情提示,
1.数据反序列化可能会远程远程传输一些我们不希望被反序列化的数据怎么办呢?
输出结果如下
2.如果序列化对象中包含非序列化对象,仍会报错
<span style="font-size:18px;">* During deserialization, the fields of non-serializable classes will
* be initialized using the public or protected no-arg constructor of
* the class. A no-arg constructor must be accessible to the subclass
* that is serializable. The fields of serializable subclasses will
* be restored from the stream. <p></span>
在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数的构造方法。可序列化子类的字段将从该流中还原。
<span style="font-size:18px;"> * When traversing a graph, an object may be encountered that does not
* support the Serializable interface. In this case the
* NotSerializableException will be thrown and will identify the class
* of the non-serializable object. <p></span>
当遍历一个图形时,可能会遇到不支持可序列化接口的对象。在此情况下,将抛出 NotSerializableException,并将标识不可序列化对象的类。
2.serialVersionUID如果没有默认值会怎样?
<span style="font-size:18px;">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.
*</span>
如果一个序列化的类没有显式声明serialVersionUID,然后序列化运行时将默认serialVersionUID值,但是ava对象序列化规范(TM)“强烈”推荐所有序列化的类声serialVersionUID值,因为默认serialVersionUID计算高度敏感的类的细节,这取决于编译器实现,可能会有所不同,并且在反序列化可能会导致意外的invalidclassexception 。这里稍后我们会测试在真正生产上的影响
3.在什么情况下我们会用到序列化?
我们这里先要明白我们为什么要序列化?序列化是为了可以进行远程传输数据,可以用于反序列化从数据流中得到原有数据,并唯一确认对象属性a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
。
。
。
测试用例说明:
a.Test1序列化与反序列化
package com.yeepay.g3.app.watch.serial;
import com.alibaba.fastjson.JSONObject;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 类名称: SerialMemo <br>
* 类描述: <br>
*
* @author: king
* @since: 16/8/24 下午2:03
* @version: 1.0.0
*/
public class SerializableTest {
//序列化操作--FileOutputStream
private static void start() throws IOException {
User user = new User("a", 6);
user.setPwd("123123");
System.out.println("序列化操纵之前");
System.out.println("user:" + user);
System.out.println("user:" + JSONObject.toJSON(user));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user"));
oos.writeObject(user);
oos.close();
}
//反序列化操作---FileInputStream
private static void end() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user"));
User user1 = (User) ois.readObject();
ois.close();
System.out.println("反序列化操作之后");
System.out.println("user1:" + user1);
System.out.println("user1:" + JSONObject.toJSON(user1));
}
public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
// start();
end();
}
}
class User implements Serializable {
private static final long serialVersionUID = 6456715177298089050L;
private String name;
private int age;
private String pwd;
public User() {
System.out.println("create User");
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
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 getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}</span>
分两次执行main方法输出结果如下:
<span style="font-size:18px;">序列化操纵之前
user:com.yeepay.g3.app.watch.serial.User@8e1dfb1
user:{"age":6,"name":"a","pwd":"123123"}
反序列化操作之后
user1:com.yeepay.g3.app.watch.serial.User@7796649
user1:{"age":6,"name":"a","pwd":"123123"}</span>
b.注释掉serialVersion分别执行main方法
//private static final long serialVersionUID = 6456715177298089050L;
序列化操纵之前
user:com.yeepay.g3.app.watch.serial.User@8e1dfb1
user:{"age":6,"name":"a","pwd":"123123"}
反序列化操作之后
user1:com.yeepay.g3.app.watch.serial.User@7796649
user1:{"age":6,"name":"a","pwd":"123123"}
我们会发现,serialVersionUID貌似别没有卵用,都执行通过了,但是事实真的如此么c.在b的基础上,再添加一个类属性然后分别执行main方法
即首次执行 start()方法,User类属性为
class User implements Serializable {
// private static final long serialVersionUID = 6456715177298089050L;
private String name;
private int age;
private String pwd;
执行end()方法,User类属性为
class User implements Serializable {
// private static final long serialVersionUID = 6456715177298089050L;
private String name;
private int age;
private String pwd;
private String memo;
end执行后,输出结果如下:
Exception in thread "main" java.io.InvalidClassException: com.yeepay.g3.app.watch.serial.User; local class incompatible: stream classdesc serialVersionUID = 3686252464883344796, local class serialVersionUID = 2579413800018730532
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at com.yeepay.g3.app.watch.serial.SerializableTest.end(SerializableTest.java:38)
at com.yeepay.g3.app.watch.serial.SerializableTest.main(SerializableTest.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)</span>
由此我们知道,当在线业务远程传出对象存在升级变更的时候,会存在对象无法反序列化
d.相反我们添加上序列编码会发现 一切正常
由此得出结论,还是能加就加上吧
友情提示,
1.数据反序列化可能会远程远程传输一些我们不希望被反序列化的数据怎么办呢?
private transient String pwd;// 标记属性
输出结果如下
序列化操纵之前
user:com.yeepay.g3.app.watch.serial.User@650b5efb
user:{"age":6,"name":"a","pwd":"123123"}
反序列化操作之后
user1:com.yeepay.g3.app.watch.serial.User@49c0c8b3
user1:{"age":6,"name":"a"}
2.如果序列化对象中包含非序列化对象,仍会报错
Exception in thread "main" java.io.NotSerializableException: com.yeepay.g3.app.watch.serial.Memo