序列化的作用:为了不同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();
}
}
}
运行结果:
序列化相关注意事项:
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 +
'}';
}
}
运行结果:
这个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 +
'}';
}
}
运行结果:
加入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();
}
}
}
运行结果:
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();
}
}
}
运行结果:
Box类中实现readResolve() 方法可以实现单例对象还是同一个。
参考资料