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}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。