Java序列化

本文详细介绍了Java序列化的概念,如何通过Serializable接口实现,以及serialVersionUID在版本控制和兼容性中的作用,包括处理不同版本类的序列化和反序列化过程中的异常情况。

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

Java序列化

Java 序列化是一种将对象转换为字节流的过程,以便可以将对象保存到磁盘上,将其传输到网络上,或者将其存储在内存中,以后再进行反序列化,将字节流重新转换为对象。

要使一个类可序列化,需要让该类实现 java.io.Serializable接口。

简单的实现:

public static void main(String[] args) throws IOException, ClassNotFoundException {
    output();
    System.out.println();
    input();
}

public static void output() throws IOException {
    A a = new A();
    a.x = 100;
    a.b = new B();
    System.out.println(a);  // org.example.serial.A@1b6d3586
    System.out.println(a.x);  // 100
    System.out.println(a.b);  // org.example.serial.B@4554617c
    
    FileOutputStream fos = new FileOutputStream("A.ser");
    ObjectOutput output = new ObjectOutputStream(fos);

    output.writeObject(a);

    output.close();
    fos.close();
}

public static void input() throws IOException, ClassNotFoundException {
    FileInputStream fis = new FileInputStream("A.ser");
    ObjectInputStream ois = new ObjectInputStream(fis);

    A a = (A) ois.readObject();
    System.out.println(a);  // org.example.serial.A@568db2f2
    System.out.println(a.x);  // 100
    System.out.println(a.b);  // null

    fis.close();
    ois.close();
}

class A implements Serializable {
    private static final long serialVersionUID = 1L;
    int x;
    transient B b;
}

class B implements Serializable {

}

其中transient代表转瞬即逝的,被其修饰的变量值不会被发送到输出流,所以反序列化后的属性为对应的0值

版本号

在反序列化读取或者是序列化传输反序列化接收时,JVM会把传来的字节流中的serialVersionUID与本地相应实体 (类)的serialVersionUID进行比较,如果相同就认为是一致的, 可以进行反序列化,否则就会出现序列化版本不一致的异常。

例如上述代码中,写入的A.ser文件中版本序列号为1,现将其变为2,并仅运行input(),则报错如下:Exception in thread "main" java.io.InvalidClassException: org.example.serial.A; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2


如果在序列化时没有指定版本号,JVM会生成版本号。

// 对此类执行序列化
class A implements Serializable {
    int x;
    transient B b;
}
output();

----
    
// 减少属性后执行反序列化
class A implements Serializable {
    int x;
}
input();

如果我们将没有指定版本号的类序列化到A.ser中,再在增减属性后尝试反序列化,则报错如下:Exception in thread "main" java.io.InvalidClassException: org.example.serial.A; local class incompatible: stream classdesc serialVersionUID = -8586817179612647591, local class serialVersionUID = -4435318764304933042。说明JVM在序列化时给定了序列号-8586817179612647591,而当前JVM中的(即减少属性后的)A类的序列号为-4435318764304933042,发生冲突。且生成序列号的方式不是随机的,猜测是根据字节码文件哈希得来的。


而只要版本号相同,增减属性,也能反序列化。增加的属性会赋值为0值,减少的属性会被忽略(即已没法访问)。

// 对此类执行序列化
class A implements Serializable {
    private static final long serialVersionUID = 2L;
    int x;
    transient B b;
}
output();
System.out.println(a);  // org.example.serial.A@1b6d3586
System.out.println(a.x);  // 100
System.out.println(a.b);  // org.example.serial.B@4554617c


// 保持版本号一致,减少属性后执行反序列化
class A implements Serializable {
    private static final long serialVersionUID = 2L;
    int x;
}
input();
System.out.println(a); // org.example.serial.A@58372a00
System.out.println(a.x); // 100


// 保持版本号一致,增加属性后执行反序列化
class A implements Serializable {
    private static final long serialVersionUID = 2L;
    int x;
    transient B b;
    String d;
}
input();
System.out.println(a); // org.example.serial.A@58372a00
System.out.println(a.x); // 100
System.out.println(a.b);  // null
System.out.println(a.d);  // null  因为序列化时没这个值,直接给null

总之序列化版本号如其意,就是用来控制类的不同版本,相同版本才能去兼容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值