【JAVA】序列化与反序列化

序列化和反序列化

序列化是指将对象的状态信息转化为可传输或存储的形式的过程,一般转化为二进制流或者xml形式,与序列化相对应的过程称之为反序列化,它是指将二进制流或者xml形式转化为对象的过程。

序列化的作用

  1. 对象信息的持久化存储;
  2. 对象信息的网络传输;
  3. 使程序更具有可维护性?
  4. 可以弥补操作系统,语言之前的差异性;

序列化格式

1.二进制字节;
2.xml格式;
3.json格式;

JAVA的序列化

序列化接口——Serializable

java.io.Serializable接口(标记接口,不包括任何方法):JVM会将实现了Serializable接口的对象转化为字节序列,并能够将这个字节序列完全恢复为原来的对象;

序列化与反序列化简单的实现:

public class Index {
    @Test
    public void serializeObj() throws IOException {
        PersonEntity entity = new PersonEntity();
        entity.setName("Bob");
        FileOutputStream fileInputStream = new FileOutputStream("E://temp/a.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileInputStream);
        objectOutputStream.writeObject(entity);
        objectOutputStream.close();
        fileInputStream.close();
    }

    @Test
    public void deserializeObj() throws IOException, ClassNotFoundException {
        PersonEntity entity = new PersonEntity();
        FileInputStream fileInputStream = new FileInputStream("E://temp/a.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        entity = (PersonEntity) objectInputStream.readObject();
        System.out.println("entity = " + entity);
        objectInputStream.close();
        fileInputStream.close();
    }
}
package com.basic.practice.serialize;

import java.io.Serializable;
import java.util.Objects;
import java.util.StringJoiner;

public class PersonEntity implements Serializable {
    private static final long serialVersionUID = 865294760793762612L;
    private String name;

    private Integer age;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return new StringJoiner(", ", PersonEntity.class.getSimpleName() + "[", "]")
                .add("name='" + name + "'")
                .add("age=" + age)
                .toString();
    }
}

对象(包括String、Integer等包装类)实现SerializaSeble接口,根据某个流创建ObjectOutputStream对象,调用writeObject就可以进行序列化;

反序列化特点:对SerializaSeble对象进行还原的过程中,没有调用任何构造器(包括默认构造器),对象是通过从InputStream中取二进制位恢复过来的;

注意
①int等基础数据类型也可以进行序列化;
②可序列化类的子类型都是可序列化的;
③序列化的类有父类型,想要序列化父类中的成员,父类需要实现SerializaSeble接口;
④static修饰的成员变量不会被序列化;

序列化版本ID——serialVersionUID

serialVersionUID:JAVA序列化机制在运行时根据判断类的serialVersionUID,来验证版本是否一致;JVM会把传来的字节流中的serialVersionUID和本地相应实体的serialVersionUID进行相应比较,如果相同,就认为版本一致,可以进行反序列化,否则不能进行反序列化,并抛出序列化ID不一致的异常(java.io.InvalidClassException);

凡是实现了SerializaSeble接口接口的类,都有一个long类型的静态变量-序列化版本ID;如果没有显示的声明这个serialVersionUID变量,java序列化机制会根据编译的class文件自动生成一个serialVersionUID,只有同一次编译的class才会生成相同的serialVersionUID;

应用场景:
①在某些场景下,希望类的不同版本对于序列化兼容,因此要确保不同版本类的serialVersionUID序列化一致,例如:dubbo版本的服务升级时,api中的实体升级兼容;
②在某些场景下,不希望类的不同版本对于序列化兼容,因此要确保不同版本类的serialVersionUID序列化不一致;

实现类的部分属性序列化的方法

transient关键字

类实现Serializable接口,并用transient关键字修饰不需要序列化的成员变量;

transient关键字仅修饰变量,不修饰类和方法;

序列化时,类实例中被transient关键字修饰的成员变量的序列化;反序列化时,被transient关键字修饰的成员变量不会被持久化和恢复;

实现Externalizable接口

通过实现java.io.Externalizable接口进行序列化,该接口继承了SerializaSeble接口,并且添加了2个方法writeExternal()和readExternal(),方法会在序列化和反序列化的过程中分别自动被调用,以便执行特殊操作;

实现Externalizable接口对象在恢复过程中会调用默认构造方法(默认构造方法必须public,没有对应默认构造方法时,反序列化会抛出错误java.io.OptionalDataException),然后调用readExternal() 接口;

注意:
①writeExternal(ObjectOutput out)方法中序列化成员属性时,使用的write方法需要和readExternal(ObjectInput in)方法中反序列化时使用的read方法一一对应,例如:使用out.writeInt(age)进行序列化Integer类型的age,就必须使用in.readInt()来进行反序列化恢复age,而不能使用(Integer) in.readObject()来恢复,否则会抛出异常(java.io.OptionalDataException);
②readExternal(ObjectInput in)在反序列化时,反序列化恢复成员的顺序和成员属性的数量,必须和writeExternal(ObjectOutput out)方法序列化成员时保持一致,否则会抛出异常(java.io.EOFException)

package com.basic.practice.serialize;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

/**
 * @auther 
 * @date 7/5/2020 23:54
 * @description
 **/
public class Man implements Externalizable {
    private String name;
    private String address;
    private Integer age;

    public Man() {
    }

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

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.name = (String)in.readObject();
        this.age = in.readInt();
    }

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Man{");
        sb.append("name='").append(name).append('\'');
        sb.append(", address='").append(address).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }
}

添加writeObject()和readObject()方法

类实现Serializable接口,并且向类中添加writeObject()和readObject()方法,这两个方法必须是private和void修饰;

原理:JAVA调用ObjectOutputStream类序列化时,会检查类其是否有私有的,且无返回值的writeObject方法,如果有,其会委托该方法进行对象序列化,源码如下:

ObjectStreamClass.java
...
...
    void invokeWriteObject(Object obj, ObjectOutputStream out)
        throws IOException, UnsupportedOperationException
    {
        requireInitialized();
        if (writeObjectMethod != null) {
            try {
                writeObjectMethod.invoke(obj, new Object[]{ out });
            } catch (InvocationTargetException ex) {
                Throwable th = ex.getTargetException();
                if (th instanceof IOException) {
                    throw (IOException) th;
                } else {
                    throwMiscException(th);
                }
            } catch (IllegalAccessException ex) {
                // should not occur, as access checks have been suppressed
                throw new InternalError(ex);
            }
        } else {
            throw new UnsupportedOperationException();
        }
    }
...
...
package com.basic.practice.serialize;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @auther 
 * @date 7/5/2020 23:40
 * @description
 **/

public class People implements Serializable {
    private String firstName;
    private int age;
    private String birthDay;

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.writeObject(firstName);
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        this.firstName = (String) ois.readObject();
    }

    public String getBirthDay() {
        return birthDay;
    }

    public void setBirthDay(String birthDay) {
        this.birthDay = birthDay;
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("People{");
        sb.append("firstName='").append(firstName).append('\'');
        sb.append(", age=").append(age);
        sb.append(", birthDay='").append(birthDay).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值