Java自定义序列化行为解析

本文介绍如何通过自定义序列化和反序列化行为解决Java类中不可序列化的状态类问题,确保序列化后数据完整性和一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The original website:http://longdick.iteye.com/blog/1018051

正常情况下,一个类实现java序列化很简单,只需要implements Serializable接口即可,之后该类在跨jvm的传输过程中会遵照默认java序列化规则序列化和反序列化;不同jvm版本之间序列化方式稍有不同,但基本上都是兼容的。

在某些特殊情况下,可能需要自定义序列化和反序列化的行为,看下面例子:

 

Java代码 复制代码   收藏代码
  1. class AbstractSerializeDemo {   
  2.     private int x, y;   
  3.   
  4.     public void init(int x, int y) {   
  5.         this.x = x;   
  6.         this.y = y;   
  7.     }   
  8.   
  9.     public int getX() {   
  10.         return x;   
  11.     }   
  12.   
  13.     public int getY() {   
  14.         return y;   
  15.     }   
  16.   
  17.     public void printXY() {   
  18.         System.out.println("x:" + x + ";y:" + y);   
  19.     }   
  20. }   
  21.   
  22. public class SerializeDemo extends AbstractSerializeDemo implements Serializable {   
  23.     private int z;   
  24.   
  25.     public SerializeDemo() {   
  26.         super.init(1050);   
  27.         z = 100;   
  28.     }   
  29.   
  30.     public void printZ() {   
  31.         super.printXY();   
  32.         System.out.println("z:" + z);   
  33.     }   
  34.   
  35.     public static void main(String[] args) throws IOException, ClassNotFoundException {   
  36.         ByteArrayOutputStream bos = new ByteArrayOutputStream();   
  37.         ObjectOutputStream out = new ObjectOutputStream(bos);   
  38.         SerializeDemo sd = new SerializeDemo();   
  39.         sd.printZ();   
  40.         out.writeObject(sd);   
  41.         ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));   
  42.         SerializeDemo sd2 = (SerializeDemo) in.readObject();   
  43.         sd2.printZ();   
  44.     }   
  45. }  
class AbstractSerializeDemo {
    private int x, y;

    public void init(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void printXY() {
        System.out.println("x:" + x + ";y:" + y);
    }
}

public class SerializeDemo extends AbstractSerializeDemo implements Serializable {
    private int z;

    public SerializeDemo() {
        super.init(10, 50);
        z = 100;
    }

    public void printZ() {
        super.printXY();
        System.out.println("z:" + z);
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        SerializeDemo sd = new SerializeDemo();
        sd.printZ();
        out.writeObject(sd);
        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        SerializeDemo sd2 = (SerializeDemo) in.readObject();
        sd2.printZ();
    }
}

 

 

 

这段程序表示了一个可序列化的类继承自一个非序列化的有状态超类,期望的结果是,子类序列化以后传输并反序列化回来,原先的值域包括超类的值域都保持不变。

但是输出是:

Java代码 复制代码   收藏代码
  1. x:10;y:50  
  2. z:100  
  3. x:0;y:0  
  4. z:100  
x:10;y:50
z:100
x:0;y:0
z:100

 

 

 

结果和期望不符,子类的值域保留下来了,但是超类的值域丢失了,这对jvm来说是正常的,因为超类不可序列化;

为了解决这个问题,只能自定义序列化行为,具体做法是在SerializeDemo里加入以下代码:

 

 

Java代码 复制代码   收藏代码
  1. private void writeObject(ObjectOutputStream os) throws IOException {   
  2.       os.defaultWriteObject();//java对象序列化默认操作   
  3.       os.writeInt(getX());   
  4.       os.writeInt(getY());   
  5.   }   
  6.   
  7.   private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {   
  8.       is.defaultReadObject();//java对象反序列化默认操作   
  9.       int x=is.readInt();   
  10.       int y=is.readInt();   
  11.       super.init(x,y);   
  12.   }  
  private void writeObject(ObjectOutputStream os) throws IOException {
        os.defaultWriteObject();//java对象序列化默认操作
        os.writeInt(getX());
        os.writeInt(getY());
    }

    private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
        is.defaultReadObject();//java对象反序列化默认操作
        int x=is.readInt();
        int y=is.readInt();
        super.init(x,y);
    }

 

 

 

writeObject和readObject方法为JVM会在序列化和反序列化java对象时会分别调用的两个方法,修饰符都是private,没错。

我们在序列化的默认动作之后将超类里的两个值域x和y也写入object流;与之对应在反序列化的默认操作之后读入x和y两个值,然后调用超类的初始化方法。

再次执行程序之后的输出为:

 

Java代码 复制代码   收藏代码
  1. x:10;y:50  
  2. z:100  
  3. x:10;y:50  
  4. z:100  
x:10;y:50
z:100
x:10;y:50
z:100


 

另外还有两个自定义序列化方法writeReplace和readResolve,分别用来在序列化之前替换序列化对象 和 在反序列化之后的对返回对象的处理。一般可以用来避免singleTon对象跨jvm序列化和反序列化时产生多个对象实例,事实上singleTon的对象一旦可序列化,它就不能保证singleTon了。JVM的Enum实现里就是重写了readResolve方法,由JVM保证Enum的值都是singleTon的,所以建议多使用Enum代替使用writeReplace和readResolve方法。

 

 

Java代码 复制代码   收藏代码
  1. private Object readResolve()   
  2.     {   
  3.         return INSTANCE;   
  4.     }   
  5.       
  6.     private Object writeReplace(){   
  7.         return INSTANCE;   
  8.     }  
private Object readResolve()
    {
        return INSTANCE;
    }
   
    private Object writeReplace(){
        return INSTANCE;
    }


 

注:writeReplace调用在writeObject前;readResolve调用在readObject之后。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值