JAVA序列化Serializable及Externalizable区别详解
序列化简介
Java的对象序列化将那些实现Serializable接口的对象转换成一个字节序列,并能在之后将这个字节序列完全恢复为原来的对象。
这就意味着Java对象在网络上的传输可以不依赖于当前计算机的操作系统,就可以将对象进行传递,这也是Java跨平台的一种体现。
Java对象的序列化主要支持两种特性:
1、Java的远程方法调用(RemoteMethodInvocationRMI);
2、对于JavaBean来说,序列化也是必须的。
要序列化一个对象,需要创建一个OutputStream对象,然后将其封装在ObjectOutputStream对象中,再调用writeObject()方法就可以完成对象的序列化(也是在这一步进行序列化);反序列化(将一个序列还原为一个对象)就是该过程的反过程:创建一个InputStream对象,将其封装在ObjectInputStream对象中,使用readObject()方法将序列反序列化为对象,当然这是一个Object类型的对象,需要向下转型为我们需要的类型(如果该类型不在本地,会导致反序列化失败,ClassNotFoundException)。
先说结论
序列化有以下方式:
1、实现Serializable接口:
2、实现Externalizable接口,并重写writeExternal()readExternal()方法;
3、(即下文中的Externalizable的替代方式进行序列化)如果不想实现Externalizable接口,又想按照自己的规则进行序列化,可以实现Serializable接口,并在该类中添加(添加,不是覆盖、实现)名为writeExternal()readExternal()方法,且这两个方法必须为下面这两个准确的方法签名:
privatevoidwriteObject(ObjectOutputStreamstream)throwsIOException;
privatevoidreadObject(ObjectInputStreamstream)throwsIOException,ClassNotFoundException;
一、三种方式完成序列化
1、实现Serializable接口序列化
这种方式最为常用且常见,只需要对需要序列化的类实现Serializable即可,对于不希望进行序列化的,可以使用transient关键词进行修饰(即瞬时变量)。
这种方式序列化的特征:
1、Serializable接口仅仅是一个标记接口,不包含任何方法;
2、对于Serializable对象来说,对象完全以它存储的二进制位为基础来构造,(反序列化)不会调用构造器。
2、实现Externalizable接口序列化
这种方式可以实现序列化的完全自定义:所有成员变量是否序列化都需要在writeExternal()、readExternal()
方法中写出;且可以完全自定义序列化方式(在writerExternal()、readExternal()方法中)。当然,实现Externalizable接口必须要重写这两个方法。
这种方式序列化的特征:
1、必须重写writerExternal()、readExternal()两个方法,并在两个方法中写出所有需要序列化的成员变量;
2、对于Externalizable对象来说,必须要有无参public构造器,不然会报出InvalidClassException异常。
3、Externalizable的替代方式进行序列化
让ObjectOutputStream和ObjectInputStream对象的writeObject()方法和readObject()方法调用我们编写的这两个方法。
如果想在这种方式中也调用原有默认提供的方式,可以在writeObject()中调用:s.defaultWriteObject();,在readObject()中调用s.defaultReadObject();。这部分代码可以查看ArrayList源码。
二、测试代码
1、Serializable对象反序列化,不调用任何构造器
Serializable对象反序列化不调用任何构造器,包括默认构造器,整个对象都是从InputStream中取得数据恢复过来的
主测试类Dogs
publicclassDogs{
publicstaticvoidmain(String[]args)throwsException{
//创建对象
System.out.println("---创建对象---");
Dog1d1=newDog1("pidan",4.0);
Dog2d2=newDog2("duanwu","black");
//序列化
System.out.println("---序列化---");
ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("D:/dogs.out"));
oos.writeObject(d1);
oos.writeObject(d2);
System.out.println("---反序列化---");
//反序列化不会调用任何构造器
ObjectInputStreamois=newObjectInputStream(newFileInputStream("d:/dogs.out"));
Dog1o1=(Dog1)ois.readObject();
Dog2o2=(Dog2)ois.readObject();
System.out.println("反序列化o1:"+o1);
System.out.println("反序列化o2:"+o2);
}
}
Serializable对象Dog1Dog2类
classDog1implementsSerializable{
privatestaticfinallongserialVersionUID=-7101743601344663182L;
privateStringname;
privateDoubleweight;
publicDog1(Stringname,Doubleweight){
System.out.println("Dog1构造器运行---");
this.name=name;
this.weight=weight;
System.out.println("Dog1:"+this);
}
//省略get、set、toString方法
}
publicclassDog2implementsSerializable{
privatestaticfinallongserialVersionUID=-5462607652670703938L;
privateStringname;
privateStringcolor;
publicDog2(Stringname,Stringcolor){
System.out.println("Dog2构造器运行---");
this.name=name;
this.color=color;
System.out.println("Dogs2:"+this);
}
//省略get、set、toString方法
}
运行结果:
---创建对象---
Dog1构造器运行---
Dog1:Dog1{name='pidan',weight=4.0}
Dog2构造器运行---
Dogs2:Dog2{name='duanwu',color='black'}
---序列化---
---反序列化---
反序列化o1:Dog1{name='pidan',weight=4.0}
反序列化o2:Dog2{name='duanwu',color='black'}
再最后取出对象时,完全没有调用到其任何构造器。
2、无参构造器对Externalizable对象序列化的影响
主测试代码:
publicclassPersons{
publicstaticvoidmain(String[]args)throwsException{
//创建对象
System.out.println("InitObjects");
Person1p1=newPerson1();
Person2p2=newPerson2();
//存储在磁盘上
ObjectOutputStreamos=newObjectOutputStream(newFileOutputStream("d:/person.out"));
os.writeObject(p1);
os.writeObject(p2);
os.flush();
os.close();
//取出
ObjectInputStreamis=newObjectInputStream(newFileInputStream("d:/person.out"));
System.out.println("取出p1:");
p1=(Person1)is.readObject();
p2=(Person2)is.readObject();
}
}
Externalizable对象:Perion1Persion2
publicclassPerson1implementsExternalizable{
publicPerson1(){
System.out.println("Person1构造器---");
}
@Override
publicvoidwriteExternal(ObjectOutputout)throwsIOException{
System.out.println("Person1writeExternal---");
}
@Override
publicvoidreadExternal(ObjectInputin)throwsIOException,ClassNotFoundException{
System.out.println("Person1readExternal---");
}
}
classPerson2implementsExternalizable{
//注意不是public
Person2(){
System.out.println("Person2构造器---");
}
@Override
publicvoidwriteExternal(ObjectOutputout)throwsIOException{
System.out.println("Person2writeExternal---");
}
@Override
publicvoidreadExternal(ObjectInputin)throwsIOException,ClassNotFoundException{
System.out.println("Person2readExternal---");
}
}
Person2默认构造器不是public的运行结果:
InitObjects Person1构造器--- Person2构造器--- Person1writeExternal--- Person2writeExternal--- 取出p1: Person1构造器--- Person1readExternal--- Exceptioninthread"main"java.io.InvalidClassException:...serializableAndexternalizable.Person2;novalidconstructor atjava.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:169) atjava.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:874) atjava.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2043) atjava.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573) atjava.io.ObjectInputStream.readObject(ObjectInputStream.java:431) at...serializableAndexternalizable.Persons.main(Persons.java:29) Processfinishedwithexitcode1
将Person2构造器改为public后:
InitObjects
Person1构造器---
Person2构造器---
Person1writeExternal---
Person2writeExternal---
取出p1:
Person1构造器---
Person1readExternal---
Person2构造器---
Person2readExternal---
3、使用Externalizable对象实现序列化
主测试类Cats:
publicclassCats{
publicstaticvoidmain(String[]args)throwsException{
//初始化对象
System.out.println("---初始化对象---");
Personperson=newPerson("01","老王",30);
Cat2cat=newCat2("fugui",person);
//序列化
System.out.println("---序列化对象---");
ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("d:/cats.out"));
oos.writeObject(cat);
System.out.println("---反序列化对象---");
ObjectInputStreamois=newObjectInputStream(newFileInputStream("d:/cats.out"));
cat=(Cat2)ois.readObject();
System.out.println("---反序列化对象后---");
System.out.println("cat:"+cat);
}
}
Externalizable对象:Cat2;Serializable对象:Person:
publicclassPersonimplementsSerializable{
privatestaticfinallongserialVersionUID=-822166081906894628L;
privatetransientStringid;
privateStringname;
privateintage;
publicPerson(){
System.out.println("---Person无参构造器---");
}
publicPerson(Stringid,Stringname,intage){
System.out.println("---Person无参构造器---");
this.id=id;
this.name=name;
this.age=age;
System.out.println("Person:"+this);
}
//省略get、set、toString方法
}
classCat2implementsExternalizable{
privatestaticfinallongserialVersionUID=1102930161606017855L;
privateStringname;
privatePersonminion;
publicCat2(){
System.out.println("Cat2无参构造器--->");
}
publicCat2(Stringname,Personminion){
System.out.println("Cat2有参构造器--->");
this.name=name;
this.minion=minion;
System.out.println("Cat2:"+this);
}
//省略get、set、toString方法
@Override
publicvoidwriteExternal(ObjectOutputout)throwsIOException{
System.out.println("---Cat2:writeExternal---");
//code1
out.writeObject(this.minion);
out.writeObject(this.name);
}
@Override
publicvoidreadExternal(ObjectInputin)throwsIOException,ClassNotFoundException{
System.out.println("---Cat2:readExternal---");
//code2
this.minion=(Person)in.readObject();
this.name=(String)in.readObject();
}
}
运行结果:
可以注意到Person的成员变量id在使用了transient关键词修饰后,就不再序列化该字段了。
---初始化对象---
---Person无参构造器---
Person:Person{id='01',name='老王',age=30}
Cat2有参构造器--->
Cat2:Cat2{name='fugui',minion=Person{id='01',name='老王',age=30}}
---序列化对象---
---Cat2:writeExternal---
---反序列化对象---
Cat2无参构造器--->
---Cat2:readExternal---
---反序列化对象后---
cat:Cat2{name='fugui',minion=Person{id='null',name='老王',age=30}}
如果将Cat2类中标注的code1与code2代码下面的两行代码均注释掉,就不再可以序列化及反序列化了:
注释掉后的运行结果:
---初始化对象---
---Person无参构造器---
Person:Person{id='01',name='老王',age=30}
Cat2有参构造器--->
Cat2:Cat2{name='fugui',minion=Person{id='01',name='老王',age=30}}
---序列化对象---
---Cat2:writeExternal---
---反序列化对象---
Cat2无参构造器--->
---Cat2:readExternal---
---反序列化对象后---
cat:Cat2{name='null',minion=null}
4、使用Externalizable对象替代方式实现序列化
替代方式就是实现Serializable接口,并且添加writeObject(),readObject()两个方法注意这两个方法必须有准确的方法特征签名,在这两个方法中编写自定义方式实现序列化和反序列化。
classMouseimplementsSerializable{
privatestaticfinallongserialVersionUID=-3278535893876444138L;
privateStringname;
privateinti;
publicMouse(){
System.out.println("Mouse无参构造器---");
}
publicMouse(Stringname,inti){
System.out.println("Mouse有参构造器---");
this.name=name;
this.i=i;
System.out.println("Mouse:"+this);
}
//方法特征签名必须完全一致
privatevoidwriteObject(ObjectOutputStreamstream)throwsIOException{
//stream.defaultWriteObject();//可以选择执行默认的writeObject()
System.out.println("---这是自定义的writeExternal方法---");
stream.writeObject(this.name);
stream.writeInt(this.i);
}
//方法特征签名必须完全一致
privatevoidreadObject(ObjectInputStreamstream)throwsIOException,ClassNotFoundException{
//stream.defaultReadObject();//可以选择执行默认的readObject()
System.out.println("---这是自定义的readExternal方法---");
this.name=(String)stream.readObject();
this.i=stream.readInt();
}
//省略get、set、toString方法
}
主测试类:
publicclassMouses{
publicstaticvoidmain(String[]args)throwsException{
//创建对象
System.out.println("---创建对象---");
Mousem1=newMouse("zhizhi",2);
//序列化
System.out.println("---序列化---");
ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("D:/mouse.out"));
oos.writeObject(m1);
//反序列化
System.out.println("---反序列化---");
ObjectInputStreamois=newObjectInputStream(newFileInputStream("d:/mouse.out"));
//反序列化结果
System.out.println("---反序列化结果---");
m1=(Mouse)ois.readObject();
System.out.println("zhizhi:"+m1);
}
}
运行结果
---创建对象---
Mouse有参构造器---
Mouse:Mouse{name='zhizhi',i=2}
---序列化---
---这是自定义的writeExternal方法---
---反序列化---
---反序列化结果---
---这是自定义的readExternal方法---
zhizhi:Mouse{name='zhizhi',i=2}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。