- 什么是java系列化?
为了对象能够在网络间传输(如rpc,rmi等),将对象转换为二进制流的过程称为序列化。同样的,将二进制流转换为对象的过程称为反序列化。 - Java系列化的几种方法?
- 实现Serializable接口
这是大家最常见的一种也是最简单的一种方式,该接口没有任何方法,实现了该接口的对象可以直接使用ObjectOutputSteam的writeObject方法写到流里面,然后调用ObjectInputStream的readObject方法将其读出来。这里因为大家都知道也熟悉,我就不贴代码了,主要看下面两种方式。 - 实现Externalizable接口
首先我们看下该接口的结构
这个接口里面方法,方别对应的是写对象到流的writeExternal和从流中读对象的readExternal方法,这个相比直接Serializable实现而言,这里可以控制更多的细节,比如你可以将一个transient字段也保存起来。下面看代码
package com.hxw.serialize;
import java.io.*;
public class ExternalizableDemo implements Externalizable{
private char value ;
private transient int count ;
public ExternalizableDemo(char value,int count){
System.out.println("ExternalizableDemo External Constructor");
this.value = value;
this.count = count;
}
public ExternalizableDemo(){
System.out.println("ExternalizableDemo Default Constructor");
}
@Override
public String toString() {
return "ExternalizableDemo{" +
"value=" + value +
", count=" + count +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("write object");
out.writeObject(value);
out.writeObject(count);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("read object");
this.value = (char)in.readObject();
this.count = (int)in.readObject();
}
public static void test() throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SeriallizeTest.FILE));
oos.writeObject(new ExternalizableDemo('a',1));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SeriallizeTest.FILE));
Object obj = ois.readObject();
System.out.println(obj);
}
}
这里有两点需要大家注意
2.1第一种和下面的第三种方式读对象的时候是都不会调用构造器,而这种方式会调用构造器,所以必须要要有一个默认的构造器
2.2读和写的顺序必须要一致,否则会出现前后值对不上的问题,第三种同样存在
- 实现Serializable接口并增加writeObject和readObject方法
注意,这里是增加不是覆盖。直接看代码
package com.hxw.serialize;
import java.io.*;
public class SerialExtend implements Serializable{
private String a;
private String d;
private transient String b;
private transient String c;
public SerialExtend(String a, String b, String c, String d) {
System.out.println("construct");
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public SerialExtend() {
System.out.println("default construct");
}
private void writeObject(ObjectOutputStream out) throws Exception{
out.defaultWriteObject();
out.writeObject(b);
out.writeObject(c);
}
private void readObject(ObjectInputStream in)throws Exception{
in.defaultReadObject();
b = (String)in.readObject();
c = (String)in.readObject();
}
@Override
public String toString() {
return "SerialExtend{" +
"a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public static void test() throws Exception{
SerialExtend sc = new SerialExtend("test1","test2","test3","test4");
System.out.println("before:" + sc);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(buf);
o.writeObject(sc);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
SerialExtend sc2 = (SerialExtend) in.readObject();
System.out.println("after:"+sc2);
}
}
这个方式其实和第二种方式有点类似的,都可以控制细节,并且也会产生顺序问题,而且defalutRead和defaultWrite(自动处理非transient字段)也可以换成我们的普通read和write同样也能完成工作。这里和第二点不同的就是这里不会调用构造器。
- 扩展点
- 对象写进文件后的内容
其实认真看ASCII码,虽然有点乱码,但是不能猜出其实它存的就是对象的信息,包括属于哪个类,每个字段的值等(个人猜测,还未验证) - 同一对象使用同一输入流调用两次write后,后面读两次的话,读的对象也是同一个,可以用上面的额随便一个demo测试同一对象使用同一输入流调用两次write后,后面读两次的话,读的对象也是同一个,可以用上面的随便一个demo测试