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

本文详细介绍了Java中的序列化与反序列化过程,包括序列化的概念、实现方式(如Student类的示例),以及序列化版本号的作用和Java分类机制。特别强调了transient关键字和序列化版本号在确保兼容性的重要性,以及手动指定序列化版本号的建议。
摘要由CSDN通过智能技术生成

序列化和反序列化

序列化
   1.将内存中的Java对象传输或者存储到硬盘文件或其他设备文件上的一个过程。
   2.在Java对象传输的过程中有可能是跨网络传输,此时Java对象被拆分成无数个数据后一个个得传输。
   3.拆分的过程就是将内存中的Java对象转化为字节流的过程。
   4.序列化也称"拆分对象"
   
反序列化
   1.将存储在硬盘文件上的Java对象再重新恢复到内存中的过程。
   2.反序列化也称"组装对象"

在这里插入图片描述
参与序列化和反序列化的对象,必须实现Serializable接口,但该接口中什么代码也没有,它只是一个标识,标识给JVM看的。JVM看到这个接口之后,会为该类自动生成一个序列化版本号。没有实现该接口时,报错“NotSerializableException”
在这里插入图片描述

序列化的实现

Java对象Student类

public class Student implements Serializable {
    
    private int no;
    private String name;

    省略setter和getter
    
    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }
}

序列化实现测试类

public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws IOException {
        //创建Java对象
        Student s = new Student(211,"雷磊");
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
        //序列化对象
        oos.writeObject(s);
        //刷新
        oos.flush();
        //关闭
        oos.close();
    }
}

当Student类没有实现Serializable接口时,报错NotSerializableException
在这里插入图片描述
在这里插入图片描述

反序列化的实现

public class ObjectInputStreamTest {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
        //开始序列化,读我们序列化时创建的‘students’文件
        Object object = ois.readObject();
        System.out.println(object);
        ois.close();
    }
}

在这里插入图片描述

序列化多个对象

public class ObjectOutputStreamList {
    public static void main(String[] args) throws Exception{
        //创建多个Java对象
        List<User> list = new ArrayList<>();
        list.add(new User(18,"张三"));
        list.add(new User(16,"狗剩"));
        list.add(new User(17,"翠花"));

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
        oos.writeObject(list);
        oos.flush();
        oos.close();
    }
}
public class ObjectInputStreamList {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
//        Object obj = ois.readObject();
//        System.out.println(obj instanceof List);
        List<User> list = (List<User>) ois.readObject();
        for (User user : list){
            System.out.println(user);
        }
        ois.close();
    }
}
public class User implements Serializable {

    private int age;
    private String name;

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    getter和setter省略
}

在这里插入图片描述
一次序列化多个对象时,参与序列化的ArrayList集合以及集合中的对象User都需要实现“java.io.Serializable”
ArrayList源码中已经实现了Serializable接口。
在这里插入图片描述

transient关键字

transient关键字表示游离的,不参与序列化

在这里插入图片描述
在这里插入图片描述

idea生成序列化版本号

在这里插入图片描述

序列化版本号的作用

如果不指定版本号,当代码后期有修改,修改之后再重新编译就会由如下报错,提示序列化版本不一致。

Connected to the target VM, address: '127.0.0.1:59813', transport: 'socket'
Exception in thread "main" java.io.InvalidClassException:
 com.lei.serialize.pojo.User; 
 local class incompatible: 
 stream classdesc serialVersionUID = 3903506952886969445, 
 local class serialVersionUID = 3407519326099955091
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2028)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1875)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2209)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466)
	at java.util.ArrayList.readObject(ArrayList.java:799)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1185)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2345)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2236)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466)
	at com.lei.serialize.ObjectInputStreamList.main(ObjectInputStreamList.java:19)
Disconnected from the target VM, address: '127.0.0.1:59813', transport: 'socket'

Process finished with exit code 1

Java区分类的机制

Java语言中是采用什么机制来区分类的?
  第一:首先通过类名进行对比,如果类名不一样,肯定不是同一个类。
  第二:如果类名一样,就靠序列化版本号进行区分。

  如果两个程序员编写了同一个类名的Java对象Student,这个时候序列化版本就起作用了。
  对于Java虚拟机来说,java虚拟机可以通过序列化来区分两个类,这要这俩个类都实现了Serializable接口,序列号版本号不一样所以就区分了。

注意:
   这种有Java虚拟机自动生成的序列化版本号有缺陷:一旦代码确定之后,不能进行后续的修改;
   因为只有修改,必然会重新编译,此时就会生成新的序列化版本号,
   而此时Java虚拟机就会认为这是一个全新的类了,因为序列化版本号不一致了,所以就会报错。
   所以建议建议手动指定序列化版本号,写死在Java对象的代码里(自由Idea自动生成)private static final long serialVersionUID = 2551410408049453941L;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值