【24】序列化

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

序列化

文章目录

1.概念

1.1序列化

(1)将数据结构或对象转换成二进制串的过程.

1.2反序列化

(1)将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

1.3持久化

(1)把数据结构或对象存储起来,以便下次还能够从存储的地方反序列化出来。

1.4序列化一般会用在什么地方?

在这里插入图片描述

(1)如果需要把一个对象传递到另外一台机器或者是另外一个进程里面,这个过程就存在一个跨进程传输或网络传输,这个时候就需要把对象序列化之后才能传输。

1.5序列化方案

(1)Serializable(Java的序列化实现方案)
(2)Parcelable(Android独有的)
(3)json,xml,protobuf…(二进制数据,广义的序列化)

2.如何合理的选择序列化方案?

2.1通用性

这个序列化方案,是不是能够跨平台,Android的数据能不能传给iOS用,能不能传给Linux系统用,能不能传给Windows系统用.

2.2强健性/鲁棒性

容错的情况怎么样,是不是很容易导致出现错误。

2.3可调试性/可读性

(1)序列化之后是否可读,可读性好不好,比如Serializable序列化之后是二进制的,对于人阅读就不是很好。而json、xml、人类就很好理解。

(2)性能

  • 可扩展性/兼容性
  • 安全性/访问限制

3.Java中实现序列化的方案

在这里插入图片描述

3.1实现Serializable接口

(1)标识 tags

(2)如何实现序列化的?

  • 主要是通过输入输出流实现的.
  • ObjectOutput
  • ObjectInput
  • ObjectStreamClass

反序列化即是通过ObjectInputStream实现的

3.1.1定义需要序列化的对象
public class Person implements Serializable{

    private static final long serialVersionUID = 1L;

    private int age;

    /**
     * transient用于指定成员变量不以字节流的方式保存到文件
     */
    private transient String name;

    private double height;

    public Person(int age,String name, double height){
        this.age = age;
        this.name = name;
        this.height = height;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + age + ", name='" + name + '\'' + ", height=" + height + '}';
    }
}
3.1.2定义序列化实现工具类
public class SerizlizableUtils {

    /**
     * 持久化
     * @param obj
     * @param path
     */
    public static synchronized boolean saveObject(Object obj,String path){
        if(null == obj){
            return false;
        }

        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(path));
            oos.writeObject(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null != oos){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return false;
    }

    /**
     * 反序列化对象
     * @param path
     * @param <T>
     * @return
     */
    public static synchronized <T> T readObject(String path){
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(path));
            return (T) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(null != ois){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
3.1.3测试序列化
    public static void main(String[] args) {
        Person p = new Person(22,"李丽华",168);
        //1.持久化对象到文件
        SerizlizableUtils.saveObject(p,"E:/seriademo.txt");

        //2.反序列化对象
        Person rp = SerizlizableUtils.readObject("E:/seriademo.txt");
        System.out.println(rp);
    }

3.2实现Externalizable接口

(1)writeExternal(ObjectOutput out)
(2)readExternal(ObjectInput in)

3.2.1定义序列化对象类
public class Person1 implements Externalizable {

    private int age;

    private String name;

    private double height;

    public Person1(){

    }

    public Person1(int age,String name, double height){
        this.age = age;
        this.name = name;
        this.height = height;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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

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

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.age = in.readInt();
        this.name = (String) in.readObject();
        this.height = in.readDouble();
    }
}
3.2.2测试序列化与反序列化
public class ExternalizableDemo {
    public static void main(String[] args) {
        Person1 p = new Person1(22,"李丽华",168);
        System.out.println("1: " + p);

        ObjectOutputStream oos = null;
        byte[] personData = null;


        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            oos = new ObjectOutputStream(out);
            oos.writeObject(p);
            personData = out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null != oos){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        ByteArrayInputStream bas = new ByteArrayInputStream(personData);
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(bas);
            p = (Person1) ois.readObject();
            System.out.println("2:" + p);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
3.2.3Externalizable序列化注意点

(1)在writeExternal与readExternal方法中读写成员变量顺序要要求一致。
(2)在writeExternal与readExternal方法中读写成员变量个数也要求一致。
(3)如果不一致,则会报java.io.EOFException。
(4)序列化的对象必须要有一个无参的构造函数。(为什么需要?看源码)

java.io.InvalidClassException: com.gdc.serializabledemo.entity.Person1; no valid constructor	

4.关于序列化的几个问题

4.1什么是serialVersionUID?如果你不定义这个,会发生什么?

(1) serialVersionUID是一个private static final long 型ID,当它被印在对象上时,它通常是对象的哈希码,你可以使用serialver这个JDK工具来查看序列化对象的serialVersionUID。

(2)SerialVersionUID用于对象的版本控制。

(3)可以在类文件中指定serialVersionUID,不指定SerialVersionUID的后果是,当添加或修改类中的任何字段时,则已序列化的类将无法恢复。因为为新类和旧序列化对象生成的serialVersionUID将有所不同。

(4)自己整理:

serialVersionUID用于对象的版本控制,不定义的话,会导致添加或修改类中的字段时,已序列化的类将无法恢复,因为为新类和旧序列化对象生成的serialVersionUID将有所不同。

4.2假设你有一个类,它序列化并存储在持久性中,然后修改了该类以添加新字段。如果对已序列化的对象进行反序列化,会发生什么情况?

(1)Java序列化过程依赖于正确的序列化对象恢复状态,并在序列化对象序列版本不匹配的情况下引发java.io.InvalidClassException无效类异常。

(2)把serialVersionUID改为2

java.io.InvalidClassException: com.gdc.serializabledemo.entity.Person; 
local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

(3)自己整理:
如果修改了serialVersionUID,会造成序列化对象的序列版本不区配情况,在反序列化过程中会引发java.io.InvalidClassException无效类异常。

4.3序列化时,希望某些成员不要序列化?如何实现它?

(1)什么时瞬态transient变量?瞬态变量和静态变量会不会得到序列化等,所以如果不希望任何字段是对象的状态的一部分,可以声明它为静态或瞬态的。这样不会在Java序列化过程中被包含在内。

(2)即在变量声明时为其添加transient关键字,让其不被序列化。

4.4如果类中的一个成员未实现可序列化接口,会发生什么情况?

如果尝试序列化实现序列化的类的对象,但该对象包含对不可序列化类的引用,则在运行时将引发不可序列化异常NoSerializableException.

Caused by: java.io.NotSerializableException: com.gdc.serializabledemo.entity.NickName

4.5如果类是可序列化的,但其超类不是,则反序列化后从超级类继承的实例变量的状态如何?

(1)Java序列化过程仅在对象层次都是可序列化结构中继续,即实现Java中的可序列化接口,并且从超级类继承的实例变量的值将通过调用构造函数初始化,在反序列化过程中不可序列化的超级类。

(2)实验证明

  • 如果子类是可序列化的,但它的父类没有实现序列化接口,在反序列化过程中,父类的实例变量将为默认值(0或null),即父类的变量的值将不会被序列化。

  • 此时如果需要实现父类变量的序列化,则需要在子类中实现readObject()和writeObject()方法。

4.5.1案例

(1)父类

package com.gdc.serializabledemo.entity;

public class Person2 {
    private int id;
    private String sex;

    public Person2() {
    }

    public Person2(int id, String sex) {
        this.id = id;
        this.sex = sex;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

(2)子类

public class User extends Person2 implements Serializable {

    private String name;
    private int age;

    public User(){

    }

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

    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;
    }

    private void writeObject(ObjectOutputStream out) throws Exception{
        out.defaultWriteObject();
        out.writeObject(getSex());
        out.writeInt(getId());
    }

    private void readObject(ObjectInputStream in)throws Exception{
        in.defaultReadObject();
        setSex((String) in.readObject());
        setId(in.readInt());
    }

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

(3)测试

public class SerializableTest1 {

    public static void serializableObj(){
        User u = new User("李丽华",22,"男",1);
        SerizlizableUtils.saveObject(u,"E:/serialtest2.txt");
    }

    public static void deserizlizableObj(){
        User u = SerizlizableUtils.readObject("E:/serialtest2.txt");
        System.out.println(u);
    }

    public static void main(String[] args) {
        serializableObj();
        deserizlizableObj();
    }
}
4.5.2注意

(1)父类需要有无参构造函数

4.6是否可以自定义序列化的过程,或者是否可以覆盖Java中的默认序列化的过程?

(1)可以自定义,需要覆盖readObject方法与writeObject()方法。
(2)不是重写的父类的方法(它是什么时候调用的呢?看源码java.io.ObjectStreamClass#hasWriteObjectData,通过反射)

    private void writeObject(ObjectOutputStream out) throws Exception{
        out.defaultWriteObject();
        out.writeObject(getSex());
        out.writeInt(getId());
    }

    private void readObject(ObjectInputStream in)throws Exception{
        in.defaultReadObject();
        setSex((String) in.readObject());
        setId(in.readInt());
    }

4.7假设新类的超级类实现可序列化接口,如何避免新类被序列化?

(1)如果父类实现了Serializable,而子类没有实现Serializable,则序列化与反序列化的过程都是没问题的。

(2)如果要避免子类被序列化,就需要在子类中实现writeObject、readObject、readObjectNoData()方法,即在其中抛出异常,即说明在子类中不允许序列化。

private void writeObject(ObjectOutputStream out) throws Exception{
        throw new NotSerializableException("Can not serialize this class");
    }

    private void readObject(ObjectInputStream in)throws Exception{
        throw new NotSerializableException("Can not serialize this class");
    }

    private void readObjNoData() throws ObjectStreamException{
        throw new NotSerializableException("Can not serialize this class");
    }

4.8在Java中的序列化和反序列化过程中使用哪些方法?

(1)readObject()

(2)writeObject()

(3)readExternal()

(4)writeExternal()

(5)Java序列化由Java.io.ObjectOutputStream类完成。该类是一个筛选器流,它封装在较低级别的字节流中,以处理序列化机制。

(6)要通过序列化机制存储任何对象,调用ObjectOutputStream.writeObject(saveObj)。

(7)反序列化对象调用ObjectInputStream.readObject(),调用以writeObject()方法在java中触发序列化过程。

(8)关于readObject()方法,需要注意的一点是它用于从持久性读取字节,并从这些字节创建对象,并返回一个对象,该对象需要类型强制转换为正确的类型。

5.序列化流程

在这里插入图片描述

6.枚举序列化

(1)Java的序列化机制针对枚举类型是特殊处理的。简单来廛,在序列化枚举类型时,只会存储枚举类的引用和枚举常量的名称。随后的反序列化的过程中,这些信息被用来在运行时环境中查找存在的枚举类型对象。

7.单例类防止序列化

/**
 * 单例类防止反序列化
 */
public class Singleton implements Serializable {

    private Singleton(){

    }

    /**
     * 通过添加readResolve直接返回之前的单例
     * @return
     */
    private Object readResolve(){
        return SingletonHolder.instance;
    }
    
    public static class SingletonHolder{
        public static Singleton instance = new Singleton();
    }
}
/**
 * 单例类防止反序列化
 */
public class Singleton1 implements Serializable {
    
    private static Singleton1 instance = new Singleton1();
    
    private Singleton1(){

    }

    /**
     * 通过添加readResolve直接返回之前的单例
     * @return
     */
    private Object readResolve(){
        return instance;
    }
}

8.单例类如何防止反射?

/**
 * 单例类防止反序列化
 */
public class Singleton2 implements Serializable {

    public static boolean flag = false;

    private Singleton2(){
        synchronized (Singleton.class){
            if(flag){
                flag = !flag;
            }else{
                //如果再次被实例化之后,则抛出一个运行时异常
                throw new RuntimeException("单例模式被侵犯!");
            }
        }
    }

    public static class SingletonHolder{
        public static Singleton2 instance = new Singleton2();
    }

    public static Singleton2 getInstance(){
        return SingletonHolder.instance;
    }
}

9.Android的Parcelable

9.1实现案例

public class User implements Parcelable {

    private String name;
    private int age;

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    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 static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
}

10.Parcelable与Serializable的区别

SerializableParcelable
通过IO对硬盘操作,速度较慢直接在内存操作,效率高,性能好
大小不受限制一般不能超过1M,修改内核也只能4M
大量使用反射,产生内存碎片

11.面试题

11.1反序列化后的对象,需要调用构造函数重新构造吗?

反序列化是直接从二进制文件中解析出来的,解析出来之后进行强转的,反序列化的时候不会调用到构造方法。只有序列化的过程调用构造方法。

11.2序列前的对象与序列化后的对象是什么关系?是("=="还是equals?是浅复制还是深复制?)

对象在序列化与反序列化前后的存放地址是不一样的。枚举是个例外。

11.3Android里面为什么要设计出Bundle而不是直接用Map结构?

(1)Bundle内部实际上是使用了ArrayMap,它的存储效率方面要比HashMap要好。

    static {
        EMPTY = new Bundle();
        EMPTY.mMap = ArrayMap.EMPTY;

        STRIPPED = new Bundle();
        STRIPPED.putInt("STRIPPED", 1);
    }

11.4SerialVersionID的作用是什么?

用于对象的版本控制

11.5Android中Intent/Bundle的通信原理及大小限制是多少?

11.6为何Intent不能直接在组件间传递对象而要通过序列化机制?

Activity之间的通信,需要跟AMS进行交互,而AMS进程与Activity进程不是同一个进程,所以需要实现跨进程通信,因此就需要序列化。

11.7序列化与持久化的关系和区别是什么?

(1)序列化是为了实现进程间数据交换。
(2)而持久化是为了将数据存储下来。

12.打赏鼓励

感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!

12.1微信打赏

在这里插入图片描述

12.2支付宝打赏

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值