序列化与反序列化

对象的序列化和反序列化

​ 如果需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些都需要用到序列化,那么什么是序列化呢?

1.序列化与反序列化定义

  • 序列化(Serialize):将数据结构或对象转换成二进制字节流的过程
  • 反序列化(DeSerialize):将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程

序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

而如果我们需要序列化一个JAVA对象,就需要使用到一个类,就是前面我们所提到的ObjectOutputStream,同理,反序列化需要用到的类是ObjectInputStream

2.代码示例

首先创建一个Student类

public class Student implements Serializable {
//    private static final long serialVersionUID = -8630796682164638346L;
    private int sno;
    private String name;

    public Student(int sno, String name) {
        this.sno = sno;
        this.name = name;
    }

    public int getSno() {
        return sno;
    }
    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    public Student() {
    }
}

对这个类进行序列化和反序列化

public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws IOException {
        //创建java对象
        Student student=new Student(127,"lingstar");
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student"));
        //序列化对象
        oos.writeObject(student);
        //刷新流
        oos.flush();
        //关闭流
        oos.close();

    }
}

可以看到在文件中生产了student文件

image-20220727152928835

而在文件中显示的乱码,我们想要获取内容必须进行反序列化

public class ObjectInputStreamTest01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"));
        //开始反序列化
        Object obj=ois.readObject();
        //反序列化回来是一个学生对象,所以会调用学生对象的toString方法
        System.out.println(obj);
        ois.close();
    }
}

可以看到我们反序列化之后,成功读取到的数据

Student{sno=127, name='lingstar'}

3.参与序列化的对象必须实现接口Serializable

public interface Serializable {
}

​ 这个接口起到一个标识的作用,java虚拟机看有类类实现了这个接口,就会对其进行一些特殊待遇。这种标识接口虽然里面什么都没有,但是java虚拟机看到这个接口之后会自动生产一个序列化版本号!

​ 序列化版本号是java虚拟机默认提供的,可以用来区分类。当不同的人即使编写了同一个类,在java虚拟机中通过序列化版本号也会区分!

​ 自动生产版本号的缺点:一旦代码确定之后不能进行后期更改!如果修改的话会生成一个新的序列化版本号,这是java虚拟机会认为这是一个新的类!

所以,当类实现了一个Serializable接口,我们建议手动给一个固定的版本号!这样即使这个类代码修改了,但是版本号不变,java虚拟机会认为是同一个类。

4.idea设置自动生成序列化版本号

在idea中,我们也可以为类生成一个序列化版本号

idea64_8cROGGzpTt

设置完上面之后只需在idea中对着需要进行生成序列化版本号的类,ait+回车

idea64_vcOz677zob

然后点解生成即可,这样生产的序列号有全球唯一性!

image-20220727153746298

5.一次序列化多个对象

​ 我们在序列化的时候,怎么进行对多个对象进行序列化,只需要把对象放到List集合中即可

还是创建一个类,叫Teacher类,类中的属性和Student类一样,代码省略,接着存入数据,对其进行序列化

public class ObjectOutputStreamTest02 {
    public static void main(String[] args) throws IOException {
        ArrayList<Teacher> list = new ArrayList<>();
        list.add(new Teacher(1,"老王"));
        list.add(new Teacher(2,"老张"));
        list.add(new Teacher(3,"老李"));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher"));
        oos.writeObject(list);
        oos.flush();
        oos.close();
    }
}

可以看到,我们在生产的teacher文件中存了多个对象

随后进行反序列化

public class ObjectInputStreamTest02 {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher"));
        //可以判断返回的对象是否是一个list集合
//        Object obj=ois.readObject();
//        System.out.println(obj instanceof List);
        //当判断是list集合,只需要对其进行强转即可
        List<Teacher> list=(List<Teacher>)ois.readObject();
        for (Teacher teacher :list) {
            System.out.println(teacher);
        }
        ois.close();
    }
}

然后进行输出:

输出结果:

Teacher{sno=1, name='老王'}
Teacher{sno=2, name='老张'}
Teacher{sno=3, name='老李'}

6.transient关键字

​ 在序列化的过程中,拿刚才的Student类来说,如果我们指向序列化一个sno属性,而不想要序列化name属性,应该怎么去做?

这个时候就需要使用transient关键字,transient关键字表示游离的,用transient修饰的属性,不参与序列化

我们可以看一下具体的

代码示例:

public class User implements Serializable {
    //序列化版本号
    private static final long serialVersionUID = 8551105198411139621L;
    private int sno;
    private transient String name;

    public User() {
    }

    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

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

可以看出,我们在name属性中加入了transient关键字

接下来,我们还是传入几个数据对对象进行序列化

public class ObjectOutputStreamTest03 {
    public static void main(String[] args) throws  Exception{
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user"));
        ArrayList<User> user = new ArrayList<>();
        user.add(new User(127,"小凌"));
        user.add(new User(128,"小星"));
        user.add(new User(127,"小李"));
        oos.writeObject(user);
        oos.flush();
        oos.close();

    }
}

然后再进行反序列化

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
public class ObjectInputStreamTest03 {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user"));
        List<User> list = (List<User>)ois.readObject();
        for (User user : list) {
            System.out.println(user);
        }
        ois.close();

    }
}

可以看一下输出结果:

User{sno=127, name='null'}
User{sno=128, name='null'}
User{sno=127, name='null'}

可以看到输出的集合中,并没有name的值,说明name没有被序列化!

posted @ 2022-07-27 16:16  星余明  阅读(73)  评论(0编辑  收藏  举报