Java对象的序列化和反序列化

1.序列化/反序列化一个未实现序列化接口的类型对象

如果这个类(称其为A类)提供了带参数的构造器,那么可以创建一个实现了序列化接口的新类(称其为B类)。B类中用属性来持有创建A类对象所需要的那些参数。这样问题就从序列化/反序列化一个A类对象转为了序列化/反序列化创建一个A类对象所需要的参数。

示例代码如下:

public class A {
   private String name;
   private int age;
 
   public A(int age, String name) {
       this.age = age;
       this.name = name;
    }
 
   public int getAge() {
       return age;
    }
 
   public String getName() {
       return name;
    }
 
   @Override
   public String toString() {
       return "A{" +
                "age=" + age +
                ", name='" + name +'\'' +
                '}';
    }
}

A类没有实现序列化接口。

设计一个B类“持有”一个A类,并且实现了序列化接口。当需要序列化一个A类对象的时候,可以通过一个序列化一个B对象,来序列化A对象的属性内容。

public class B implements Serializable{
   private String name;
   private int age;
   private A a;
 
   public B(String name, int age) {
       this.name = name;
       this.age = age;
    }
   private void writeObject(ObjectOutputStream out) throws IOException{
       out.writeUTF(name);
       out.writeInt(age);
    }
private voidreadObject(ObjectInputStream in)
 throwsIOException,ClassNotFoundException{
       name = in.readUTF();
       age = in.readInt();
       a = new A(age,name);
    }
   public String toString() {
       return "B{a= " + a + ", name='" + name + '\'' +", age=" + age + '}';
    }
   public A getA(){
       return new A(age,name);
    }
}


这里需要注意的是B类里面有两个“特殊”的方法writeObject(ObjectOutputStream)和readObject(ObjectInputStream),当进行序列化/反序列化操作的时候,如果Java发现要序列化/反序列化的对象包含这样两个方法,则会优先使用这两个方法进行序列化/反序列化操作。即序列化B对象的时候,会调用B类的writeObject方法来进行而不使用ObjectOutputStream的writeObject方法。反序列化B对象的时候,会调用B类的readObject方法来进行而不使用ObjectInputStream的readObject方法。另外,通过ObjectOutputStream和ObjectInputStream包含的writeXXX方法和readXXX方法,可以知道尽管ObjectOutputStream继承自OutputStream,但是它的行为方式却是FilterOutputStream,ObjectInputStream也是这样的。而且与DataOutputStream/DataInputStream更加类似的是,反序列化时的读取属性顺序要与序列化时的存储属性顺序和方式一致。

下面是使用B类对象来间接序列化/反序列化A类对象的代码示例:

public class ObjectStreamTest {
   public static void main(String[] args) throws Exception {
       ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("d:/test.dat"));
       A a = new A(29,"Tony");
       B b = new B(a.getName(),a.getAge());
       out.writeObject(b);
       out.close();
       out = null;
       System.out.println("序列化完毕");
       ObjectInputStream in = new ObjectInputStream(new FileInputStream("d:/test.dat"));
       B bb = (B) in.readObject();
       System.out.println(bb);
       System.out.println(bb.getA());
       in.close();
    }
}


2.     外部化 Externalization

可以把外部化看做一种自定义式的序列化操作。一个类实现了Externalizable接口就意味着这个类的对象是外部化的。Externalizable接口中有两个抽象方法writeExternal和readExternal,分别用来序列化和反序列化一个可外部化的对象时被调用。

下面是外部化类对象的使用示例:

public class A implements Externalizable{
   private String name;
   private int age;
   public A() {
    }
   public A(int age, String name) {
       this.name = name;
       this.age = age;
    }
   public int getAge() {
       return age;
    }
   public String getName() {
       return name;
    }
   @Override
   public String toString() {
       return "A{" +
                "age=" + age +
                ", name='" + name +'\'' +
                '}';
    }
   @Override
   public void writeExternal(ObjectOutput out) throws IOException {
       System.out.println("writeExternal方法被调用了");
       out.writeUTF(name);
       out.writeInt(age);
    }
   @Override
   public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
       System.out.println("readExternal方法被调用了");
       name = in.readUTF();
       age = in.readInt();
    }
}


写一个测试类,序列化/反序列化一个A类对象:

public class ObjectStreamTest2 {
   public static void main(String[] args) throws IOException,ClassNotFoundException {
           System.out.println("测试外部化");
           A a  = new A(30,"Tom");
           ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("d:/obj.dat"));
           out.writeObject(a);
           out.close();
           out = null;
           System.out.println("对象序列化完毕");
           ObjectInputStream in  =
new ObjectInputStream(new FileInputStream("d:/obj.dat"));
           A aa = (A) in.readObject();
           in.close();
           System.out.println("对象反序列化完毕"+aa);
    }
}


执行结果下所示:

测试外部化

writeExternal方法被调用了
对象序列化完毕
readExternal方法被调用了
对象反序列化完毕A{age=30,name='Tom'}


可见我们在使用ObjectOutputStream/ObjectInputStream序列化和反序列化对象的时候,虽然表面上都是通过writeObject/readObject方法来执行,但是执行的内容确不一样:

1)如果序列化/反序列化对象实现了外部化接口,则writeObject调用对象的writeEnternal方法,readObject调用对象的readExternal方法。

2)如果对象实现的是Serializable接口,但是对象有自己的writeObject和readObject方法,则ObjectOutputStream的writeObject方法会去调用对象自己的writeObject方法,ObjectInputStream的readObject方法会去调用对象自己的readObject方法。

3)如果对象实现的是Serializable接口,但是没有自己的writeObject和readObject方法,则ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法会执行默认的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值