一.对象的序列化及反序列化
java对象在序列化及反序列化之前,必须实现 Serializable 接口;
package com.river.entity;
import java.io.Serializable;
/**
* Created by on 2017/8/15.
*/
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
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;
}
}
1.序列化
@Test
public void test1() throws Exception{
Person p = new Person("xiaoli",12);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.obj"));
out.writeObject(p);
}
结果:
2.反序列化
@Test
public void test2() throws Exception{
ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.obj"));
Person person = (Person) in.readObject();
System.out.println(person);
}
结果略;
二.场景分析
1.若Person类未实现序列化接口:
java.io.NotSerializableException: com.river.entity.Person
2.serialVersionUID的作用
serialVersionUID的作用是用来区分序列化前后的类是否一致,若serialVersionUID的值不一致,则序列化失败;
如下序列化前该值为1L,反序列化前将该值改为2L;
java.io.InvalidClassException: com.river.entity.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
3.序列化对象有父类
1)当父类未实现序列化接口时
@Test
public void test1() throws Exception{
Person p = new Person("xiaoli",12);
p.setCreateTime(new Date());
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.obj"));
out.writeObject(p);
}
@Test
public void test2() throws Exception{
ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.obj"));
Person person = (Person) in.readObject();
System.out.println(person.getCreateTime());
}
运行结果:
null
可以看到,父类中的属性值是没有被反序列化回来的;
分析:
要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就需要有默认的无参的构造函数。 在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取 父对象的变量值时,它的值是调用父类无参构造函数后的值。如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都 是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。
4.某些字段不想序列化
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
若有大量的属性不序列化,可以考虑利用场景3的情况将它们统一封装在父类中;
5.序列化前自定义处理
例如对象中有用户名密码字段,序列化前加密处理,如何操作呢?
对象中自定义相同签名的序列化方法:
package com.river.entity;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* Created by on 2017/8/15.
*/
public class Person extends BaseEntity implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
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;
}
private void writeObject(ObjectOutputStream in){
System.out.println(1);
}
}
运行序列化方法:
1
可见调用了自定义的方法,我们就可以自定义实现了;
同理:
对象中自定义相同签名反序列化的方法,并加上解密逻辑即可实现反序列化对加密字段进行解密;
三.序列化与单例
单例模式可以被反射破坏,除了反射,序列化与反序列化也会破坏单例;
public class Singleton implements Serializable{
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
//Write Obj to file
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
oos.writeObject(Singleton.getSingleton());
//Read Obj from file
File file = new File("tempFile");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Singleton newInstance = (Singleton) ois.readObject();
//判断是否是同一个对象
System.out.println(newInstance == Singleton.getSingleton());
}
运行结果为: false ,说明单例模式与反序列化得到的对象不是同一个对象,反序列化出一个新的对象,破坏的单例性;
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
这段代码:
obj = desc.isInstantiable() ? desc.newInstance() : null;
boolean isInstantiable() {
requireInitialized();
return (cons != null);
}
private final void requireInitialized() {
if (!initialized)
throw new InternalError("Unexpected call when not initialized");
}
该类构造方法:
private ObjectStreamClass(final Class<?> cl) {
this.cl = cl;
name = cl.getName();
isProxy = Proxy.isProxyClass(cl);
isEnum = Enum.class.isAssignableFrom(cl);
serializable = Serializable.class.isAssignableFrom(cl);
externalizable = Externalizable.class.isAssignableFrom(cl);
Class<?> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
localDesc = this;
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (isEnum) {
suid = Long.valueOf(0);
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
suid = getDeclaredSUID(cl);
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}
});
} else {
suid = Long.valueOf(0);
fields = NO_FIELDS;
}
try {
fieldRefl = getReflector(fields, this);
} catch (InvalidClassException ex) {
// field mismatches impossible when matching local fields vs. self
throw new InternalError(ex);
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
} else if (cons == null) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
for (int i = 0; i < fields.length; i++) {
if (fields[i].getField() == null) {
defaultSerializeEx = new ExceptionInfo(
name, "unmatched serializable field(s) declared");
}
}
initialized = true;
}
initialized = true;
可知:
isInstantiable
:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。针对serializable和externalizable我会在其他文章中介绍。
desc.newInstance
:该方法通过反射的方式调用无参构造方法新建一个对象。
序列化会通过反射调用无参数的构造方法创建一个新的对象。
如何防止序列化反序列化破坏单例呢?
利用序列化对象内自定义反序列方法,
private Object readResolve() {
return singleton;
}
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
这样的化反序列化得到的就是单例返回的实例,而不用通过无参构造创建新对象了,避免了破坏单例;