Android对象序列化

简述

在android系统中,一个对象的可序列化可以通过两种方式来实现:一种是通过实现Serializable接口,另一种是通过实现Parcelable接口。其中Serializable接口是在Java中已有的,而Parcelable则是Android系统自有的。
首先我们先看看通过Serializable和Parcelable接口来实现对象的可序列化。然后在看看Serializable和Parcelable接口实现序列化有什么不同。

一、Serializable的使用

在这里我们实现了User类的可序列化,代码如下:

public class User implements Serializable {

    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    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;
    }
    @Override
    public String toString() {
        return "User{ name = " + name + ", age = " + age + "}";
    }
}

从上述的代码可以看到,如果需要通过Serializable接口来实现对象的可序列化,那是相当的简单,直接实现这个接口就行。由于Serializable接口是一个空接口,它只是起到标志一个类对象为可序列化的作用。

我们知道,通过Serializable接口实现的可序列化对象是可以保存在一个文件里的,并且可以通过读取这个文件将这个对象反序列化成对象。下面,我们就看看这个简单的例子。

  //序列化操作,既将对象存入文件
  private void serializable() {
        User user = new User("lxb", 4); //创建一个对象
        File file = new File(getFilesDir(), "user"); 
        ObjectOutputStream oos = null ;
        try {
            if (!file.exists()) {// 如果这个文件不存在,则创建这个文件
                file.createNewFile();
            }
            oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(user); //将上面创建的对象保存在这个文件中
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (null != oos) {
                try {
                    oos.close();
                    oos = null;
                    file = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
//反序列化操作,既将对象从这个文件中恢复(注意,这里对象实际上已经不是原来的对象,只不过它的值和原来一致)
private void deserializable() {
        File file = new File(getFilesDir(), "user");
        ObjectInputStream ois = null;
        try {
            if (file.exists()) {
                ois = new ObjectInputStream(new FileInputStream(file));
                User user = (User) ois.readObject();//读取对象
                System.out.println(user); //显示这个对象
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (null != ois) {
                try {
                    ois.close();
                    ois = null;
                    file = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

然后我们定义一个Activity来调用这两个函数

public class SerializableActivity extends AppCompatActivity implements View.OnClickListener{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_serializable);
        findViewById(R.id.btn_serializable).setOnClickListener(this);
        findViewById(R.id.btn_deserializable).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_serializable:
                serializable(); //点击这个按钮,将对象存入文件
                break;
            case R.id.btn_deserializable:
                deserializable(); //点击这个按钮,将文件内存反序列化成一个对象
                break;
            default:
                break;
        }
    }
}

在这里,我们先点击序列化这个按钮来将这个对象存入文件,然后点击反序列化按钮取出这个文件。结果如下。

org.alesjia.huntingapp I/System.out: User{ name = lxb, age = 4}

二、使用Serializable需要注意的问题

如果这个时候,我们需要给这个User添加一个地址字段,代码如下

public class User implements Serializable {
    private String name;
    private int age;
    private String address; //新增

    public User(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;//新增
    }

    //新增
    public String getAddress() {
        return address;
    }
    //新增
    public void setAddress(String address) {
        this.address = address;
    }

    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;
    }
     @Override
    public String toString() {
        return "User{ name = " + name + ", age = " + age + ", address = " + address + "}"; //改动,新增地址
    }

}

这个时候将序列化代码做如下改动


    private void serializable() {
        //User user = new User("lxb", 4);//原先代码改为如下
        User user = new User("lxb", 4, "shanghai");
        **** 其他代码不变
    }

这个时候我们直接点击反序列化按钮,结果出现如下错误

org.alesjia.huntingapp W/System.err: java.io.InvalidClassException: org.alesjia.huntingapp.bean.User; Incompatible class (SUID): org.alesjia.huntingapp.bean.User: static final long serialVersionUID =-3061720370958576609L; but expected org.alesjia.huntingapp.bean.User: static final long serialVersionUID =-2709034500598358200L;

通过这个错误,我们可以看到,是由于类的兼容性问题导致对象不能从这个文件中反序列化出来。而类的兼容性是通过serialVersionUID的相同与否来判断的。

那么什么是serialVersionUID呢?通过查看Serializable接口的说明文档,我们发现有这么一段话。

 * <p>Every serializable class is assigned a version identifier called a {@code
 * serialVersionUID}. By default, this identifier is computed by hashing the
 * class declaration and its members. This identifier is included in the
 * serialized form so that version conflicts can be detected during
 * deserialization. If the local {@code serialVersionUID} differs from the
 * {@code serialVersionUID} in the serialized data, deserialization will fail
 * with an {@link InvalidClassException}.

从上述的说明可以看到,这个serialVersionUID是一个版本标识符,它是通过对这个类成员进行hash计算生成的。也就是说,如果一个类的定义发生了变化,那么这个serialVersionUID会自动发生变化。那么有没有办法不让它发生变化呢?有的,我们可以自己在代码中指定serialVersionUID的值,如下所示。

 private static final long serialVersionUID = 0L;//这个值可以自己指定

通过代码指定serialVersionUID 值,我们可以做到在序列化前后,即使类成员变量的变化(增加或者删除),也可以尽可能的将这个对象反序列化出来。

那么下面我们还是通过代码来看看如何实现。

1)修改User类实现,指定serialVersionUID

public class User implements Serializable {
    private static final long serialVersionUID = 1L;//这里我们自己指定版本号,跟之前代码唯一区别。
    private String name;
    private int age;

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

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

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

2)修改序列化的相关代码,然后将这个对象存入文件中。

  private void serializable() {
      User user = new User("lxb", 4);
      ***********
  }

3)然后由于版本原因,我们需要给User类增加一个address字段,代码如下

public class User implements Serializable {
    private static final long serialVersionUID = 1L;//注意,这里虽然增加了address字段,但是我们版本号不变
    private String name;
    private int age;
    private String address;

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

    @Override
    public String toString() {
        return "User{ name = " + name + ", age = " + age + ", address = " + address + "}";
    }

    public String getAddress() {
        return address;
    }

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

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

}

4)然后修改序列化相关代码,点击反序列化按钮生成对象。

private void serializable() {
      User user = new User("lxb", 4, "shanghai");
      ***********
  }

最后可以看到结果如下,从这个结果可以看到,我们可以从文件中反序列化成这个对象。虽然我们在序列化时(将对象写入文件时)类的成员和反序列化时类的成员不一致(增加了一个address成员),不过这个对象还是可以被反序列化出来的。

org.alesjia.huntingapp I/System.out: User{ name = lxb, age = 4, address = null}

三、Parcelable的使用

现在来看一个简单的例子。在这里定义一个Car类,Car类里面包含一个Engine类变量。Engine类和Car都实现了Parcelable接口。代码如下

public class Engine implements Parcelable {

    private int type;

    public Engine(int type) {
        this.type = type;
    }

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

    //通过这个方法序列化该对象
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(type);
    }

    public static final Parcelable.Creator<Engine> CREATOR = new Creator<Engine>() {
        //通过这个方法来反序列化对象
        @Override
        public Engine createFromParcel(Parcel source) {
            return new Engine(source);
        }

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

    private Engine(Parcel in) {
        this.type = in.readInt();
    }

    @Override
    public String toString() {
        return "Engine { type = " + type + "}";
    }
}
public class Car implements Parcelable {

    private String brand;
    private int speed;
    private Engine engine;

    public Car(String brand, int speed, Engine engine) {
        this.brand = brand;
        this.speed = speed;
        this.engine = engine;
    }

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

    //通过该方法来序列化对象
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(brand);
        dest.writeInt(speed);
        dest.writeParcelable(engine, 0);
    }

    public static final Parcelable.Creator<Car> CREATOR = new Creator<Car>() {
        //通过该方法来反序列化对象
        @Override
        public Car createFromParcel(Parcel source) {
            return new Car(source);
        }

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

    private Car(Parcel in) {
        this.brand = in.readString();
        this.speed = in.readInt();
        //注意,对于成员变量类型为非基本数据类型的(非内置类型)的,需要该类也实现Parcelable接口。
        this.engine = in.readParcelable(this.getClass().getClassLoader());
    }

    @Override
    public String toString() {
        return "Car { brand = " + brand + ", speed = " + speed + ", engine = " + engine + "}";
    }
}

四、Serializable和Parcelable的不同点

1.通过Serializable实现对象序列化非常简单,而Parcelable则需要写大量的代码
2.通过Serializable接口实现对象的反序列化需要通过类反射来进行对象变量的赋值,同时会产生大量的临时对象,可能引起频繁的GC,所以效率比较低
3.由于Parcelabe是通过自定义代码来实现序列化和反序列化的,所以效率比较高
4.Serializable为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Parcelable为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,在内存间数据传输时推荐使用Parcelable。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值