一、序列号和反序列化定义
序列化:是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等。在网络传输过程中,可以是字节或是XML等格式。
反序列化:将字节的或XML编码格式还原完全相等的对象,这个相反的过程称为反序列化。
二、实现
可以通过实现Serializable或者Externalizable接口实现
1.通过实现Serializable接口
package com.java.basic.test.serial;
import java.io.*;
/**
* Created by Administrator on 2020/1/11.
*/
public class serial1_Serializable {
public static void main(String[] args) {
A a=new A();
a.i=1;
a.s="a";
FileOutputStream fileOutputStream=null;
FileInputStream fileInputStream=null;
try {
//将obj写入文件out1
fileOutputStream=new FileOutputStream("out1");
ObjectOutputStream objectOuptputStream=new ObjectOutputStream(fileOutputStream);
objectOuptputStream.writeObject(a);
fileOutputStream.close();
//通过文件out1读取obj
fileInputStream =new FileInputStream("out1");
ObjectInputStream objectInputStream =new ObjectInputStream(fileInputStream);
A a2= (A) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
System.out.println(a2.i);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class A implements Serializable{
int i;
String s;
}
结果:
1
2.通过实现Externalizable接口
(1)注意
实现Externalizable接口的类必须要提供一个public的无参的构造器。因为在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。
(2)实现代码
package com.java.basic.test.serial;
import java.io.*;
/**
* Created by Administrator on 2020/1/11.
*/
public class serial2_Externalizable {
public static void main(String[] args) {
B b=new B();
b.i=1;
b.s="a";
FileOutputStream fileOutputStream=null;
FileInputStream fileInputStream=null;
try {
//将obj写入文件out2
fileOutputStream=new FileOutputStream("out2");
ObjectOutputStream objectOuptputStream=new ObjectOutputStream(fileOutputStream);
objectOuptputStream.writeObject(b);
fileOutputStream.close();
//通过文件out2读取obj
fileInputStream =new FileInputStream("out2");
ObjectInputStream objectInputStream =new ObjectInputStream(fileInputStream);
B b2= (B) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
System.out.println(b2.i);
System.out.println(b2.s);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class B implements Externalizable{
//必须要有public的无参构造函数,否则报错
public B(){
}
int i;
String s;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
结果:
0
null
3.Serializable和Externalizable两者区别
Externalizable默认不保存实例化对象的属性值,而Serializable默认会保存实例化对象的属性值。
通过上面的实例可以发现,对B类进行序列化及反序列化之后得到的对象的所有属性的值都变成了默认值,即之前的那个对象的状态并没有被持久化下来。
4.Externalizable接口实现对象持久化
(1)持久化方法
通过实现重写writeExternal和readExternal两个方法可以实现序列化
(2)代码
package com.java.basic.test.serial;
import java.io.*;
/**
* Created by Administrator on 2020/1/11.
*/
public class serial2_Externalizable_Save {
public static void main(String[] args) {
C c=new C();
c.i=1;
c.s="a";
FileOutputStream fileOutputStream=null;
FileInputStream fileInputStream=null;
try {
//将obj写入文件out3
fileOutputStream=new FileOutputStream("out3");
ObjectOutputStream objectOuptputStream=new ObjectOutputStream(fileOutputStream);
objectOuptputStream.writeObject(c);
fileOutputStream.close();
//通过文件out3读取obj
fileInputStream =new FileInputStream("out3");
ObjectInputStream objectInputStream =new ObjectInputStream(fileInputStream);
C c2= (C) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
System.out.println(c2.i);
System.out.println(c2.s);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class C implements Externalizable{
//必须要有public的无参构造函数,否则报错
public C(){
}
int i;
String s;
//实现重写writeExternal和readExternal两个方法,可以选择序列化需要复制的成员
//读写顺序一定要一致,否则报错
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(i);
out.writeObject(s);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
i=in.readInt();
s= (String) in.readObject();
}
结果
1
a
三、细节
1.静态变量不参与序列化
(1)原因序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。
(2)代码
package com.java.basic.test.serial;
import java.io.*;
/**
* Created by Administrator on 2020/1/11.
*/
public class serial3_static implements Serializable {
private static final long serialVersionUID=1L;
public static int staticVar=5;
public static void main(String[] args) {
try {
//初始时staticVar=5
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("result.obj"));
out.writeObject(new serial3_static());
out.close();
//序列化后修改为10
serial3_static.staticVar=10;
ObjectInputStream oin=new ObjectInputStream(new FileInputStream("result.obj"));
serial3_static s3= (serial3_static) oin.readObject();
oin.close();
//读取静态变量
System.out.println(s3.staticVar);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:
10
2.自定义的序列化和反序列化策略
(1)通过在被序列化的类中增加writeObject和 readObject方法。
(2)writeObject和readObject方法如何被调用,以ArrayList为例
ObjectOutputStream的writeObject的调用栈为:writeObject--->writeObjecto--->writeOrdinaryObject--->writeSerialData--->invokeWriteObject
然后,invokeWriteObject:
/**
* Invokes the writeObject method of the represented serializable class.
* Throws UnsupportedOperationException if this class descriptor is not
* associated with a class, or if the class is externalizable,
* non-serializable or does not define writeObject.
*/
void invokeWriteObject(Object obj, ObjectOutputStream out)
throws IOException, UnsupportedOperationException
{
requireInitialized();
if (writeObjectMethod != null) {
try {
writeObjectMethod.invoke(obj, new Object[]{ out });
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof IOException) {
throw (IOException) th;
} else {
throwMiscException(th);
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {
throw new UnsupportedOperationException();
}
}
其中的 writeObjectMethod.invoke(obj, new Object[]{ out });的方法调用类中写的writeObject方法。
所以,如果一个类中包含writeObject和read0bject 方法,那么这两个方法是在使用0bjectoutputStream的write0bject方法和0bjectInputStream的readobject方法时,会通过反射的方式调用。