Java序列化、反序列化、serialVersionUID、transient

序列化是什么?

一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化。
换句话说,序列化只是表示对象的一种方式而已。

序列化将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的

反序列化:将字节数组重新构造成对象
序列化对象写入文件后,可以从文件中读取并反序列化。

整个过程独立JVM,意味着一个对象可以在一个平台上序列化,并在完全不同的平台上反序列化。

类实现序列化需要满足两个条件

  • 类必须实现java.io.serializable接口
  • 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为transient

如果你想知道Java标准类是否可序列化,请检查类的文档。测试很简单,如果类实现java.io.serializable接口,那么它是可序列化的;否则,它不是。

序列化对象

ObjectOutputStream类用于序列化一个对象

这里用Person对象来演示:
Person实体类:

import java.io.Serializable;

public class Person implements Serializable{

    private static final long serialVersionUID = 6815682549858613204L;
    
     
    private long id;
    private String name;
    private transient int age;
    private String gender;
    
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    
    public Person(long id, String name, int age, String gender) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    
    public Person() {
        super();
    }
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", age=" + age
                + ", gender=" + gender + "]";
    }
}

序列化测试:
将对象序列化存储到文件中

try {
            Person p = new Person(1231L, "李如花", 18, "woman");
            FileOutputStream fos = new FileOutputStream("/hello.tmp");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(p);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }

反序列化对象

ObjectInputStream类用于序列化一个对象,将对象从文件中反序列化出。

Ps: 当然这个地方需要注意类型转换。

为了演示方便,我们就对上文中已经被序列化的对象反序列化

反序列化对象测试:

try {
            FileInputStream fis = new FileInputStream("/hello.tmp");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Person person = (Person)ois.readObject();
            System.out.println(person.toString());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e){
            e.printStackTrace();
        }

Console控制台:

这是你可能会有疑惑,为什么李如花age是0,而不是18。这个时候就要说说transient的作用了。

transient是什么?

被声明为transient的属性不会被序列化,所以上文中person对象在序列化的时候就没有存储age这个属性,所以在反序列化时age属性值为默认初始值0。

serialVersionUID是什么?

Java序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
在进行反序列化,Java虚拟机会把传过来的字节流中的servialVersionUID和本地相应实体类的servialVersionUID进行比较,
如果相同就认为是一致的实体类,可以进行反序列化,否则JVM会拒绝对这个实体类进行反序列化并抛出异常。

serialVersionUID的生成方式
  • 默认的1L
  • 根据类名、接口名、成员方法以及属性等来生成一个64位的Hash字段

如果实现 java.io.Serializable接口的实体类没有显式定义一个名为serialVersionUID、类型为long的变量时,Java序列化 机制会根据编译的.class文件自动生成一个serialVersionUID,如果.class文件没有变化,那么就算编译再多 次,serialVersionUID也不会变化。

序列化会保存哪些信息

  • 序列化保存的是对象的状态
  • 不会保存静态属性
  • 不会保存对象的方法

什么样的类可以被序列化

要成功序列化类,必须满足两个条件:

  • 类必须实现java.io.serializable接口
  • 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为transient

父类序列化情况

  • 当父类实现了Serializable接口时,所有子类都可以被序列化。
  • 当一个对象的实例变量引用其他对象时,序列化该对象时也会把引用对象进行序列化。
  • 子类实现了Serializable接口,父类没有,父类中的属性不能序列化(不报错,数据丢失),但是在子类中属性仍能正确序列化。
  • 反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错
  • 反序列化时,如果serialVersionUID被修改,则反序列化时会失败

参考文章:

  • https://www.cnblogs.com/szlbm/p/5504166.html
  • https://www.tutorialspoint.com/java/java_serialization.htm

你可能感兴趣的:(Java基础)