花里胡哨的Java序列化机制

前言

通常在写实体类时都默认继承Serializable接口,但是有什么用?今天就来深入了解下java序列化机制

序列化、反序列化的概念

序列化就是将java对象转化成二进制保存到磁盘中去,反序列化就是从磁盘中读取文件流然后转成java对象

常用的方式及好处

  • 网络通讯传输java对象数据->利用序列化实现远程通信,即在网络上传送对象的字节序列
  • 永久保存java对象->实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上

Java序列化机制

  1. Serializable接口
  • 创建StudentEntity 并实现Serializable 接口
public class StudentEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    private String studentName;
    //get set toString
   

serialVersionUID有两种显示的生成方式:
一是默认的1L,比如:private static final long serialVersionUID = 1L;
二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如: private static final long serialVersionUID = xxxxL;

serialVersionUID的作用->目的是序列化对象版本控制,有关各版本反序列化时是否兼容

  • 在Test类中去实现序列化和反序列化。
public class Test {
    public static void main(String[] args) throws Exception, IOException {
        SerializeStudent();
        DeSerializeStudent();

    }
    //序列化方法
    private static void SerializeStudent() throws FileNotFoundException, IOException {
        StudentEntity studentEntity = new StudentEntity();
        studentEntity.setStudentName("Kaleldo");
        //序列化对象到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student"));
        oos.writeObject(studentEntity);
        oos.close();
        System.out.println("序列化对象成功");
    }
    //反序列化方法
    private static void DeSerializeStudent() throws FileNotFoundException, IOException{
        File file = new File("C://Test/Student");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        StudentEntity studentEntity = null;
        try {
            studentEntity = (StudentEntity)ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("反序列化对象成功"+studentEntity.toString());
    }
}
  • 结果:在C://Test 目录会发现一个Student文件 ,同时控制台输出以下内容
Connected to the target VM, address: '127.0.0.1:51227', transport: 'socket'
序列化对象成功
Disconnected from the target VM, address: '127.0.0.1:51227', transport: 'socket'
反序列化对象成功StudentEntity{studentName='Kaleldo'}
  1. Externalizable接口
  • 实现Externalizable接口需注意3点

(1)Externalizable继承自Serializable接口

(2)需重写writeExternal()与readExternal()方法

(3)实现Externalizable接口的类必须要提供一个public的无参的构造器。

public class Student1Entity implements Externalizable {

    private String studentName;

    public Student1Entity() {
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(studentName);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        studentName = (String) in.readObject();
    }
     //get set toString
    }
  • 测试
public class Test {
    public static void main(String[] args) throws Exception, IOException {
        SerializeStudent();
        DeSerializeStudent();

    }
    //序列化方法
    private static void SerializeStudent() throws FileNotFoundException, IOException {
        Student1Entity studentEntity = new Student1Entity();
        studentEntity.setStudentName("Kaleldo");
        //序列化对象到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student1"));
        oos.writeObject(studentEntity);
        oos.close();
        System.out.println("序列化对象成功");
    }
    //反序列化方法
    private static void DeSerializeStudent() throws FileNotFoundException, IOException{
        File file = new File("C://Test/Student1");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Student1Entity studentEntity = null;
        try {
            studentEntity = (Student1Entity)ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("反序列化对象成功"+studentEntity.toString());
    }
}

-输出

Connected to the target VM, address: '127.0.0.1:51328', transport: 'socket'
序列化对象成功
Disconnected from the target VM, address: '127.0.0.1:51328', transport: 'socket'
反序列化对象成功Student1Entity{studentName='Kaleldo'}

transient关键字及静态变量的序列化

  1. 通过实现Externalizable接口可以自定义序列化的属性,同样,关键字transient也可以达到相同的效果
    但是两点区别
  • transient修饰的变量,即使使用private修饰也会被序列化
  • 如果让private属性不被序列化,则使用Externalizable
  1. 静态变量不会被序列化
public class StudentEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    private String studentName;

    private static int studentNumber;
    }
  • 测试
public class Test {
    public static void main(String[] args) throws Exception, IOException {
        SerializeStudent();
        StudentEntity.setStudentNumber(12);
        DeSerializeStudent();

    }
    //序列化方法
    private static void SerializeStudent() throws FileNotFoundException, IOException {
        StudentEntity.setStudentNumber(13);
        StudentEntity studentEntity = new StudentEntity();
        studentEntity.setStudentName("Kaleldo");
        //序列化对象到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student1"));
        oos.writeObject(studentEntity);
        oos.close();
        System.out.println("序列化对象成功");
    }
    //反序列化方法
    private static void DeSerializeStudent() throws FileNotFoundException, IOException{
        File file = new File("C://Test/Student");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        StudentEntity studentEntity = null;
        try {
            studentEntity = (StudentEntity)ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("反序列化对象成功"+studentEntity.toString());
    }
}

  • 结果:即使在序列化之前修改了值为13,但是还是输出12,反序列输出的静态变量值变化了:说明静态变量没有被序列化了。
序列化对象成功
Disconnected from the target VM, address: '127.0.0.1:52125', transport: 'socket'
反序列化对象成功StudentEntity{studentName='Kaleldo'studentNumber='12'}

后话

1.实现序列化有两种方式,一实现Serializable接口,二是实现Externalizable接口,Externalizable可以自定义序列化规则
2.static、transient修饰的字段不会被序列化
3.serialVersionUID相当于版本号,如果每次序列化的serialVersionUID不同,将会报序列化出错

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章