什么是java序列化和反序列化?
java序列化是指将java中的对象(注意这里只针对对象,对方法无效)通过转化成字节序列存储在文件中,使得其能持久化(持久化就是当JVM停止后,里面的对象还能在下次启动时恢复,其永久存储在了磁盘上)。
反序列化与之相反,将存储在文件中的字节序列恢复成原来的对象。
序列化与反序列化的意义
可以将数据持久化;
能够实现远程通信,在网络上传输数据。
序列化与反序列化方法
1.实现Serializable接口
serializable是一个空的接口,相当于一个标识,实现了它的类表明此类可被序列化。其默认的序列化方法为:使用java.io.ObjectInputStream的writeObject()方法进行序列化;使用java.io.ObjectOutputStream的readObject()方法进行反序列化;测试代码如下
import java.io.Serializable;
public class SerializableTest implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
String sex;
public SerializableTest(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "SerializableTest [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
SerializableTest s = new SerializableTest("小明", 12, "男");
//序列化
File f = new File("D:/file.out");//创建要输出的文件
FileOutputStream fo = new FileOutputStream(f);//创建一个文件输出流
ObjectOutputStream oo = new ObjectOutputStream(fo);//创建一个对象输出流,接受其他输出流作为参数
oo.writeObject(s);//通过写对象的方法,将字节序列写入文件中
oo.close();//关闭对象输出流
fo.close();//关闭文件输出流
//反序列化
FileInputStream fi = new FileInputStream(f);//创建一个文件输入流
ObjectInputStream oi = new ObjectInputStream(fi);//创建一个对象输入流,接受其他输入流作为参数
SerializableTest result = (SerializableTest)oi.readObject();//通过读对象的方法,将字节序列转化为原本对象
oi.close();//关闭对象输入流
fi.close();//关闭问价输入流
System.out.println(result.toString());
}
}
输出结果为:
SerializableTest [name=小明, age=12, sex=男]
2.实现Externalizable接口
Externalizable接口继承了java.io.Serializable接口,其包含有两个方法:
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
所以我们可知序列化由writeExternal方法实现,反序列化应该由readExternal实现;下面来看具体的代码:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class ExternalizableTest implements Externalizable{
String name;
int age;
String sex;
public ExternalizableTest() {
System.out.println("无参构造器");
}
public ExternalizableTest(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
//序列化,ObjectOutput参数其实就是ObjectOutputStream所实现的一个接口
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
out.writeObject(sex);
}
@Override
//反序列化,读的顺序应该和写的顺序一致,否则会发生错误
//ObjectInput参数其实就是ObjectInputStream所实现的一个接口
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String)in.readObject();
age = in.readInt();
sex = (String)in.readObject();
}
@Override
public String toString() {
return "ExternalizableTest [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ExternalizableTest e = new ExternalizableTest("小红",12,"女");
//序列化
File f = new File("D:/file.out");//创建要输出的文件
FileOutputStream fo = new FileOutputStream(f);//创建一个文件输出流
ObjectOutputStream oo = new ObjectOutputStream(fo);//创建一个对象输出流,接受其他输出流作为参数
e.writeExternal(oo);//一个个的将e对象中的成员变量进行序列化
oo.close();//关闭对象输出流
fo.close();//关闭文件输出流
//反序列化
FileInputStream fi = new FileInputStream(f);//创建一个文件输入流
ObjectInputStream oi = new ObjectInputStream(fi);//创建一个对象输入流,接受其他输入流作为参数
e.readExternal(oi);//一个个的读出来,在方法中进行赋值
oi.close();//关闭对象输入流
fi.close();//关闭问价输入流
System.out.println(e.toString());
}
}
结果:
ExternalizableTest [name=小红, age=12, sex=女]
其实从代码来看,两者十分相似,只是Externalizable接口由程序员自己去实现需要序列化的变量,然后调用方法,Serializable接口默认实现对象中所有可序列化的变量。
说到Serializable序列化变量,java中有一个关键字可以使得变量不被默认序列化,那就是transient关键字。被transient所修饰的关键字将不会被默认序列化,但其可以手动进行序列化。
最后说说单例模式如何序列化。
在单例模式情况下,java中使用readResolve()方法来对单例模式进行序列化,在该方法中直接返回单例对象。因为使用普通的序列化方法,在从文件中读取字节流重组对象时,会创建一个对象,这违背了单例模式的规则。
我们重写一个readResolve方法,直接返回该对象实例,在进行反序列化时,JVM对于单例模式将会自动调用此方法来返回一个对象。
//如果该对象被用于序列化,可以保证对象在序列化前后保持一致
public Object readResolve(){
return instance;
}