java 序列化基础_《Java基础知识》Java序列化与反序列化详解

序列化的作用:为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制。

序列化应用场景:

1. 分布式传递对象。

2. 网络传递对象。

3. tomcat关闭以后会把session对象序列化到SESSIONS.ser文件中,等下次启动的时候就把这些session再加载到内存中。

完整案例:

importjava.io.Serializable;public class Box implementsSerializable {publicBox(){

System.out.println("调用构造Box方法");

}private String width;privateString height;publicString getWidth() {returnwidth;

}public voidsetWidth(String width) {this.width =width;

}publicString getHeight() {returnheight;

}public voidsetHeight(String height) {this.height =height;

}

@OverridepublicString toString() {return "Box{" +

"width=" + width +

", height=" + height +

'}';

}

}

importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;public classSerializableDemo {public static voidmain(String[] args){

Box myBox= newBox();

myBox.setWidth("50");

myBox.setHeight("30");try{

File file= new File("src\\demo\\knowledgepoints\\file\\foo.ser");//把对象信息写入文件中。

ObjectOutputStream oout = new ObjectOutputStream (newFileOutputStream(file));

oout.writeObject(myBox);

oout.close();//把对象信息从文件中获取出来。

ObjectInputStream oin = new ObjectInputStream(newFileInputStream(file));

Box newMyBox= (Box)oin.readObject(); //没有强制转换到Person类型

oin.close();

System.out.println(newMyBox);

}catch(Exception ex){

ex.printStackTrace();

}

}

}

运行结果:

6ffe1ed22248316abd2da274d6e00f5e.png

序列化相关注意事项:

a)序列化时,只对对象的状态进行保存,而不管对象的方法;

b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

d)  被序列化的对象需要 实现(Serializable)接口;

e) 案例:构造方法没有执行,说明生成新对象,不是通过New出来的;

Java 对象被序列化需要实现(Serializable)接口,原因:

在 我们将对象序列化的类 ObjectOutputStream 的方法 writeObject0 中可以找到答案。

以下是JDK8的部分源码,红色字体部分就是原因。

private void writeObject0(Object obj, booleanunshared)throwsIOException

{boolean oldMode = bout.setBlockDataMode(false);

depth++;try{//handle previously written and non-replaceable objects

inth;if ((obj = subs.lookup(obj)) == null) {

writeNull();return;

}else if (!unshared && (h = handles.lookup(obj)) != -1) {

writeHandle(h);return;

}else if (obj instanceofClass) {

writeClass((Class) obj, unshared);return;

}else if (obj instanceofObjectStreamClass) {

writeClassDesc((ObjectStreamClass) obj, unshared);return;

}//check for replacement object

Object orig =obj;

Class> cl =obj.getClass();

ObjectStreamClass desc;for(;;) {//REMIND: skip this check for strings/arrays?

Class>repCl;

desc= ObjectStreamClass.lookup(cl, true);if (!desc.hasWriteReplaceMethod() ||(obj= desc.invokeWriteReplace(obj)) == null ||(repCl= obj.getClass()) ==cl)

{break;

}

cl=repCl;

}if(enableReplace) {

Object rep=replaceObject(obj);if (rep != obj && rep != null) {

cl=rep.getClass();

desc= ObjectStreamClass.lookup(cl, true);

}

obj=rep;

}//if object replaced, run through original checks a second time

if (obj !=orig) {

subs.assign(orig, obj);if (obj == null) {

writeNull();return;

}else if (!unshared && (h = handles.lookup(obj)) != -1) {

writeHandle(h);return;

}else if (obj instanceofClass) {

writeClass((Class) obj, unshared);return;

}else if (obj instanceofObjectStreamClass) {

writeClassDesc((ObjectStreamClass) obj, unshared);return;

}

}// remaining cases

if (obj instanceof String) {

writeString((String) obj, unshared);

} else if (cl.isArray()) {

writeArray(obj, desc, unshared);

} else if (obj instanceof Enum) {

writeEnum((Enum>) obj, desc, unshared);

} else if (obj instanceof Serializable) {

writeOrdinaryObject(obj, desc, unshared);

} else {

if (extendedDebugInfo) {

throw new NotSerializableException(

cl.getName() + "\n" + debugInfoStack.toString());

} else {

throw newNotSerializableException(cl.getName());

}

}

}finally{

depth--;

bout.setBlockDataMode(oldMode);

}

}

如何将序列化控制在自己手中?

1. 通过关键字【transient】实现,字段值不被序列化。

改一下Box类:

importjava.io.Serializable;public class Box implementsSerializable {publicBox(){

System.out.println("调用构造Box方法");

}//关键字:transient 控制width不被序列化,保护数据。

private transientString width;privateString height;publicString getWidth() {returnwidth;

}public voidsetWidth(String width) {this.width =width;

}publicString getHeight() {returnheight;

}public voidsetHeight(String height) {this.height =height;

}

@OverridepublicString toString() {return "Box{" +

"width=" + width +

", height=" + height +

'}';

}

}

运行结果:

bad8c086a9ce117ba080796220eba49e.png

这个box的width值被擦除了。

2. writeObject()方法与readObject()方法。

importjava.io.IOException;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.io.Serializable;public class Box implementsSerializable {publicBox(){

System.out.println("调用构造Box方法");

}//关键字:transient 控制width不被序列化,保护数据。

private transientString width;privateString height;publicString getWidth() {returnwidth;

}public voidsetWidth(String width) {this.width =width;

}publicString getHeight() {returnheight;

}public voidsetHeight(String height) {this.height =height;

}private void writeObject(ObjectOutputStream out) throwsIOException {

out.defaultWriteObject();

out.writeChars(width);

}private void readObject(ObjectInputStream in) throwsIOException, ClassNotFoundException {

in.defaultReadObject();

width=in.readLine();

}

@OverridepublicString toString() {return "Box{" +

"width=" + width +

", height=" + height +

'}';

}

}

运行结果:

983af494c31e1859f0f188ff7a306723.png

加入writeObject()方法与readObject()方法后,width值又回来了,这两个方法都是私有的,已经可以基本猜测是通过反射调用方法,赋值的。

3.Externalizable 接口,自定义实现写入和读取序列化对象

importjava.io.Externalizable;importjava.io.IOException;importjava.io.ObjectInput;importjava.io.ObjectOutput;public class BoxTmp implementsExternalizable {publicBoxTmp(){

System.out.println("调用构造Box方法");

}privateString width;privateString height;publicString getWidth() {returnwidth;

}public voidsetWidth(String width) {this.width =width;

}publicString getHeight() {returnheight;

}public voidsetHeight(String height) {this.height =height;

}

@Overridepublic void writeExternal(ObjectOutput out) throwsIOException {

out.writeChars(width+","+width);

}

@Overridepublic void readExternal(ObjectInput in) throwsIOException, ClassNotFoundException {

String str=in.readLine();this.width = str.split(",")[0];this.height = str.split(",")[1];

}

@OverridepublicString toString() {return "BoxTmp{" +

"width='" + width + '\'' +

", height='" + height + '\'' +

'}';

}

}

public classExternalizableDemo {public static voidmain(String[] args){

BoxTmp boxTmp= newBoxTmp();

boxTmp.setWidth("50");

boxTmp.setHeight("30");try{

File file= new File("src\\demo\\knowledgepoints\\file\\foo.txt");//把对象信息写入文件中。

ObjectOutputStream oout = new ObjectOutputStream (newFileOutputStream(file));

oout.writeObject(boxTmp);

oout.close();//把对象信息从文件中获取出来。

ObjectInputStream oin = new ObjectInputStream(newFileInputStream(file));

BoxTmp newBoxTmp= (BoxTmp)oin.readObject(); //没有强制转换到Person类型

oin.close();

System.out.println(newBoxTmp);

}catch(Exception ex){

ex.printStackTrace();

}

}

}

运行结果:

2b2d2bc2764297dc94221b7d62b53659.png

Externalizable接口中的writeExternal和readExternal 方法可以用来自定义实现值的传递,覆盖等其他操作。

同时构造方法被执行了,说明这个类是被new出来后,由你支配。

4. readResolve()方法

importjava.io.ObjectStreamException;importjava.io.Serializable;public class Box implementsSerializable {public static Box box = newBox();publicBox(){}publicBox(String height,String width){this.height =height;this.width =width;

}public staticBox getInstance() {if(box == null){

box= new Box("20","10");

}returnbox;

}privateString width;privateString height;publicString getWidth() {returnwidth;

}public voidsetWidth(String width) {this.width =width;

}publicString getHeight() {returnheight;

}public voidsetHeight(String height) {this.height =height;

}private Object readResolve() throwsObjectStreamException {returnBox.box;

}

}

importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;public classSerializableDemo {public static voidmain(String[] args){

Box myBox=Box.getInstance();try{

File file= new File("src\\demo\\knowledgepoints\\file\\foo.ser");//把对象信息写入文件中。

ObjectOutputStream oout = new ObjectOutputStream (newFileOutputStream(file));

oout.writeObject(myBox);

oout.close();//把对象信息从文件中获取出来。

ObjectInputStream oin = new ObjectInputStream(newFileInputStream(file));

Box newMyBox= (Box)oin.readObject(); //没有强制转换到Person类型

oin.close();

System.out.println("newMyBox == myBox : "+(newMyBox ==myBox));

}catch(Exception ex){

ex.printStackTrace();

}

}

}

运行结果:

50d09f52fbda7e993d36c0f3f95412a3.png

Box类中实现readResolve() 方法可以实现单例对象还是同一个。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值