什么是java的序列化
所谓java对象的序列化就是将java对象的状态转换为字节流的过程,反序列化则是将字节流恢复成java对象的过程。
为什么要java的序列化
java的序列化主要为了java对象在网络中传输和持久化使用。
需要在网络中传输的java对象必须要经过序列化转换为二进制才能传输。比如一个java服务向异地的另一个服务发送请求,请求参数是一个java对象,通过将这个对象序列化成字节流后经过网络传输到另一个服务器,接收服务器在收到请求后通过反序列化再将字节流还原成java对象,像RMI、Socket通信等都需要用到java对象的序列化。
在因为某个对象太大又不经常使用,为了节约内存空间,或者对象保存的状态非常重要,需要在服务重启后也能可恢复等等这些情况, 经常需要java对象持久化,而这些持久化的对象也需要进行序列化处理才能持久化到文件。
如何实现序列化
序列化对象的类必须实现Serializable或Externalizable。其中Externalizable是Serializable的子接口,区别是:
- 实现Serializable的类对象,序列化时会将除transient关键字修饰的字段值序列化成字节流,而被transient关键字修饰的字段状态不被序列化,在反序列化得到的对象中transient修饰的字段将为空(对象为null,int为0,boolean为false等)
- 实现Externalizable的类对象,可以需要决定序列化哪些字段,这个接口有两个方法writeExternal(ObjectOutput out)和readExternal(ObjectInput in),通过实现这两个方法程序员可以自己决定序列化和反序列化化哪些字段,即便字段是被transient关键字修饰的,也可以在这两个方法中通过定制决定该字段是否被序列化和反序列化。
说明:java中提供transient关键字,主要为我们提供便利,只要实现Serilizable接口,在不需要序列化的属性前添加关键字transient即可,不用像实现Externalizable的类,还需要为序列化以及不序列化哪些字段写一些代码进行定制。
对象流
java通过java.io中的两个类ObjectOutputStream和ObjectInputStream两个类来实现对象与字节流之间的转换,通过ObjectOutputStream类将对象序列化成字节流,再通过ObjectInputStream将字节流反序列化成对象。
writeObject()方法是ObjectOutputStream中最重要的方法,用于对象序列化。如果对象包含其他对象的引用,则writeObject()方法递归序列化这些对象。
readObject()方法是ObjectInputStream中最重要的方法,用于对象反序列化。
举例说明
实现Serializable序列化
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class PersonTest {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
File file = new File("d:\\person.xml");
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
Person person = new Person("Mahesh","man", true, 20);
oout.writeObject(person);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Person newPerson = (Person) oin.readObject();
oin.close();
System.out.println(newPerson);
System.out.println("person == newPerson:"+ (person == newPerson));
}
}
class Person implements Serializable{
private static final long serialVersionUID = 1L;
public String name;
private String sex;
private transient boolean isMarried;
private transient int age;
public Person() {
}
public Person(String name, String sex, boolean isMarried, int age) {
this.name = name;
this.sex = sex;
this.isMarried = isMarried;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", isMarried="
+ isMarried + ", age=" + age + "]";
}
}
执行结果如下:
Person [name=Mahesh, sex=man, isMarried=false, age=0]
person == newPerson:false
从结果可以看出Person对象的name字段和sex字段被序列化,而被transient修饰的字段的状态则没有被序列化。
实现Externalizable序列化
import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class PersonTest {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
File file = new File("d:\\person.xml");
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
Person student1 = new Person("Mahesh","man", true, 20);
oout.writeObject(student1);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Person newPerson = (Person) oin.readObject();
oin.close();
System.out.println(newPerson);
}
}
class Person implements Externalizable{
private static final long serialVersionUID = 1L;
public String name;
private String sex;
private boolean isMarried;
private transient int age;
public Person() {
}
public Person(String name, String sex, boolean isMarried, int age) {
this.name = name;
this.sex = sex;
this.isMarried = isMarried;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", isMarried="
+ isMarried + ", age=" + age + "]";
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.name);
out.writeObject(this.sex);
out.writeInt(this.age);
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
//注意read数据的顺序和write数据的数据要一致,否则反序列化的字段状态将会乱掉
this.name = (String) in.readObject();
this.sex = (String) in.readObject();
this.age = in.readInt();
}
}
执行结果如下:
Person [name=Mahesh, sex=man, isMarried=false, age=20]
从执行结果可知实现Externalizable的类对象,序列化哪些字段是由在writeExternal()方法中定制的的,反序列化哪些字段是在readExternal()方法中定制的;无论字段是否被transient修饰,都可以由定制决定是否序列化和反序列化。
readResolve()方法
package com.yiibai.xstream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class PersonTest {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
File file = new File("d:\\person.xml");
Person person = Person.getInstance();
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
oout.writeObject(person);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Person newPerson = (Person) oin.readObject();
oin.close();
System.out.println(newPerson);
System.out.println("person == newPerson:" + (person == newPerson));
}
}
class Person implements Serializable{
private static final long serialVersionUID = 1L;
public String name;
private String sex;
private transient boolean isMarried;
private transient int age;
private static Person person;
private Person(String name, String sex, boolean isMarried, int age) {
this.name = name;
this.sex = sex;
this.isMarried = isMarried;
this.age = age;
}
public synchronized static Person getInstance(){
if (null == person){
person = new Person("Long", "man", true, 23);
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", isMarried="
+ isMarried + ", age=" + age + "]";
}
}
执行结果:
Person [name=Long, sex=man, isMarried=false, age=0]
person == newPerson:false
上面Person类是单例模式,但是序列化之前和反序列化之后的对象却不是同一个。如何在反序列化的过程中保证单例模式呢呢?java提供了一个私有方法readResolve(),当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了, 单例规则也就得到了保证.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class PersonTest {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
File file = new File("d:\\person.xml");
Person person = Person.getInstance();
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
oout.writeObject(person);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Person newPerson = (Person) oin.readObject();
oin.close();
System.out.println(newPerson);
System.out.println("person == newPerson:" + (person == newPerson));
}
}
class Person implements Serializable{
private static final long serialVersionUID = 1L;
public String name;
private String sex;
private transient boolean isMarried;
private transient int age;
private static Person instance;
private Person() {
}
private Person(String name, String sex, boolean isMarried, int age) {
this.name = name;
this.sex = sex;
this.isMarried = isMarried;
this.age = age;
}
public synchronized static Person getInstance(){
if (null == instance){
instance = new Person("Long", "man", true, 23);
}
return instance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", isMarried="
+ isMarried + ", age=" + age + "]";
}
private Object readResolve() {
return instance;
}
}
执行结果如下:
Person [name=Long, sex=man, isMarried=true, age=23]
person == newPerson:true