对象的序列化与反序列化

文章目录

  • 目标
  • 什么是序列化和反序列化
  • 使用Serializable
    • 序列化
    • 反序列化
    • 完整实现
  • 使用**Parcelable**
    • 序列化
    • 反序列化
    • 完整实现
  • 使用Json
    • 编写和校验Json串
    • 使用Gson
      • 序列化
      • 反序列化
    • 完整实现
  • 控制序列化和反序列化
    • @Serialization
    • @Expose
    • static关键字
    • transient关键字

目标

本文中介绍三种Android中常见的序列化的使用

  • 使用JDK提供的Serializable
  • 使用Android提供的Percelable
  • 使用Gson对Json进行使用

什么是序列化和反序列化

序列化是把对象以二进制的形式写入硬盘或者文件中,这样对象就能存入数据库、文件中或者在网络上进行传输。

反序列化是把二进制的对象数据读取出来并还原成对象的过程,这个对象的数据和序列化之前是一样的。

使用Serializable

使用Serializable方式进行序列化,需要让要序列化的对象implements Serializable接口。

  • serialVersionUID:建议主动生成一个固定的序列号。Idea或者AS中可以通过系统设置,让系统自己生成这个ID,setting—>Inspections->输入关键词UID->Serializable class...,然后在代码中,点击点击类名:Alt+Enter,就能生成UID.

    如果不主动为这个字段设值,java会以这个对象中的属性为这个字段计算一个serialVersionUID。所以如果当前对象的类中新增一个属性,那么serialVersionUID就会改变,这会导致因为serialVersionUID不一致而造成的反序列化失败。

public class Student implements Serializable {

    private static final long serialVersionUID = 2894055642774362458L;

    private transient String name;
    @Expose(serialize = false,deserialize = true)
    private int age;
    @SerializedName("goals")
    private Score score;
    public Student(){}

    public Student(String name, int age, Score score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Score getScore() {
        return score;
    }

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

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

    public void setScore(Score score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class Score implements Serializable {
    private static final long serialVersionUID = -1947844853731260432L;
    private int math;
    private int english;
    private int chinese;

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getEnglish() {
        return english;
    }

    public void setEnglish(int english) {
        this.english = english;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public Score() {
    }

    public Score(int math, int english, int chinese) {
        this.math = math;
        this.english = english;
        this.chinese = chinese;
    }

    @Override
    public String toString() {
        return "Score{" +
                "math=" + math +
                ", english=" + english +
                ", chinese=" + chinese +
                '}';
    }
}

序列化

使用ObjectOutPutStream类实现对象的序列化。

  • 打开ObjectOutputStream流:new ObjectOutputStream(OutputStream output),安卓中可以使用openFileOutput(filepath)打开OutputStream流
  • writeObject(obj):obj是实现Serializable接口的实体类对象
  • 关闭流要
Student serialStudent = new Student("花",18,new Score(98,95,95));
ObjectOutputStream output = new ObjectOutputStream(openFileInput("student.txt"));
output.writeObject(obj);
output.close();

反序列化

使用ObjectOutPutStream类实现对象的序列化。

  • 打开ObjectInputStream流:new ObjectInputStream(InputStream input),安卓中可以使用openFileInput(filepath)打开InputStream流
  • readObject():把二进制的对象数据进行返序列化
  • 关闭流要
ObjectInputStream input = new ObjectInputStream(openFileInput("student.txt"));
Student serialStudent1 = (Student) input.readObject();
input.close();

完整实现

//Serializable test:生成的 文件是16进制类型的,如果想要看文件,可以在AS中安装插件进行查看

Student serialStudent = new Student("花",18,new Score(98,95,95));

try {
    ObjectOutputStream output = new ObjectOutputStream(openFileOutput("student.txt",MODE_PRIVATE));
    output.writeObject(serialStudent);
    output.flush();
    output.close();
    
    
    ObjectInputStream input = new ObjectInputStream(openFileInput("student.txt"));
    Student serialStudent1 = (Student) input.readObject();
    input.close();
    Log.i(TAG, "onCreate: Serializable_test"+serialStudent1.toString());

} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

使用Parcelable

使用Parcelable方式进行序列化,需要让要序列化的对象implements Parcelable接口,创建,并且实现它内部的方法。

  • public static final Creator CREATOR = new Creator() {}:创建一个Creator对象,实现反序列化。
  • StudentP(Parcel in):完成反序列化的工作。
  • writeToParcel(Parcel dest, int flags):完成序列化的工作。
public class StudentP implements Parcelable {
    private String name;
    private int age;
    private ScoreP score;
    public StudentP(){}

    public StudentP(String name, int age, ScoreP score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

    public static final Creator<StudentP> CREATOR = new Creator<StudentP>() {
        @Override
        public StudentP createFromParcel(Parcel in) {
            return new StudentP(in);
        }

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public ScoreP getScore() {
        return score;
    }

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

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

    public void setScore(ScoreP score) {
        this.score = score;
    }

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

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

    @Override
    public String toString() {
        return "StudentP{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class ScoreP implements Parcelable {
    private int math;
    private int english;
    private int chinese;

    protected ScoreP(Parcel in) {
        math = in.readInt();
        english = in.readInt();
        chinese = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(math);
        dest.writeInt(english);
        dest.writeInt(chinese);
    }

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

    public static final Creator<ScoreP> CREATOR = new Creator<ScoreP>() {
        @Override
        public ScoreP createFromParcel(Parcel in) {
            return new ScoreP(in);
        }

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

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getEnglish() {
        return english;
    }

    public void setEnglish(int english) {
        this.english = english;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public ScoreP() {
    }

    public ScoreP(int math, int english, int chinese) {
        this.math = math;
        this.english = english;
        this.chinese = chinese;
    }

    @Override
    public String toString() {
        return "ScoreP{" +
                "math=" + math +
                ", english=" + english +
                ", chinese=" + chinese +
                '}';
    }
}

序列化

@Override
public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(math);
        dest.writeInt(english);
        dest.writeInt(chinese);
}

反序列化

 protected ScoreP(Parcel in) {
        math = in.readInt();
        english = in.readInt();
        chinese = in.readInt();
 }

注意:

序列化和反序列化时的顺序必须一致。比如下面这几个属性的顺序是int,String,int类型的 ,那么它的序列化和反序列化就得按照顺序writeInt和readInt,如下:

   //属性顺序 
		private int math;
   	private String name ;
    private int english;
    //序列化顺序
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(math);
        dest.writeString(name);
        dest.writeInt(chinese);
		}
		//反序列化顺序
     protected T(Parcel in) {
        math = in.readInt();
        english = in.readString();
        chinese = in.readInt();
 		}
    

完整实现

Parcelable是专门为Android定制的序列化方式,它不像Serializable方式那样频繁的操作IO,所以它的效率高很多。

//Parcelable test
Intent intent = new Intent(this , ParcelableActivity.class);
StudentP parcelableStudent = new StudentP("MAY", 17, new ScoreP(95, 90, 90));
Bundle bundle = new Bundle();
bundle.putParcelable("student",parcelableStudent);
intent.putExtra("student",bundle);
startActivity(intent);

使用Json

JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。它的层次结构简洁清晰,是网络上传输数据很好用的一个手段。Json有几个很好用的库,FastJson和Gson,本文使用Gson。

编写和校验Json串

  • 编写:在AndroidStudio中:new ->ScratchFile
  • 校验:使用beJson网站:www.beJson.com

使用Gson

Gson地址:https://github.com/google/gson

  • 添加依赖Gson库
dependencies {
    implementation 'com.google.code.gson:gson:2.10'
    }

序列化

使用Gson的toJson(obj)方法.

  • 对象的序列化
//object
        Gson gson = new Gson();
        Student student = new Student("teresa_may",16,new Score(60,60,60));
        String stuJson = gson.toJson(student);
        Log.i(TAG, "onCreate: gson_stu:"+stuJson);

  • 数组的序列化
        Student[] students = new Student[5];
        for (int i = 0;i<5;i++){
            Student s = new Student("teresa_may",16+i,new Score(60+i,60+i,60+i));
            students[i] = s;
        }
        String stus_Json = gson.toJson(students);
        Log.i(TAG, "onCreate: stu_array_json:"+stus_Json);

  • list/map/set的序列化
        //list、map、set
        List<Student> stu_list = new ArrayList<Student>(Arrays.asList(students));
        String stulist_Json = gson.toJson(stu_list);
        Log.i(TAG, "onCreate: stus_list_json:"+stus_Json);

反序列化

使用Gson的fromJson(json_str,Class)方法。

  • 对象的反序列化
        Student student_f = gson.fromJson(stuJson,Student.class);
        Log.i(TAG, "onCreate: gson_from_json:"+student_f.toString());

  • 数组的反序列化
        Student[] students_f = gson.fromJson(stus_Json,Student[].class);
        Log.i(TAG, "onCreate: students_from_json:"+students_f.toString());
  • list/map/set的反序列化

略有不同。需要使用Type对ArrayList的参数化类型进行明确才能进行反序列化。

        Type type = new TypeToken>(){}.getType();
        ArrayList students_list = gson.fromJson(stulist_Json,type);
        Log.i(TAG, "onCreate: students_list from json:"+students_list.toString());

完整实现

       //Gson test
        //object
        Gson gson = new Gson();
        Student student = new Student("teresa_may",16,new Score(60,60,60));
        String stuJson = gson.toJson(student);
        Log.i(TAG, "onCreate: gson_stu:"+stuJson);
        Student student_f = gson.fromJson(stuJson,Student.class);
        Log.i(TAG, "onCreate: gson_from_json:"+student_f.toString());

        //array
        Student[] students = new Student[5];
        for (int i = 0;i<5;i++){
            Student s = new Student("teresa_may",16+i,new Score(60+i,60+i,60+i));
            students[i] = s;
        }
        String stus_Json = gson.toJson(students);
        Log.i(TAG, "onCreate: stu_array_json:"+stus_Json);
        Student[] students_f = gson.fromJson(stus_Json,Student[].class);
        Log.i(TAG, "onCreate: students_from_json:"+students_f.toString());

        //list、map、set
        List<Student> stu_list = new ArrayList<Student>(Arrays.asList(students));
        String stulist_Json = gson.toJson(stu_list);
        Log.i(TAG, "onCreate: stus_list_json:"+stus_Json);

        Type type = new TypeToken<ArrayList<Student>>(){}.getType();
        ArrayList<Student> students_list = gson.fromJson(stulist_Json,type);
        Log.i(TAG, "onCreate: students_list from json:"+students_list.toString());

控制序列化和反序列化

@Serialization

用于属性上表示该属性在json中的key值,如果不使用这个注解,那么在序列化和反序列化的时候使用属性的名字作为key。有时候json串中的key存在java中的关键字时,可以使用这个注解为当前属性和json串中的key进行映射。

@SreializedName("class")
private String cls;

json中的样子:
{"class":"Student.class"}

@Expose

@Expose控制对象的序列化和反序列化,有两个属性:serializedeserialize ,默认值为true,表示允许序列化和反序列化。

@Expose(serialize = false,deserialize = true)//字段不参与序列化,允许反序列化

static关键字

static关键字修饰的字段不会被序列化,反序列化时内部的值是该字段类型的默认值。

transient关键字

static关键字修饰的字段不会被序列化和反序列化

你可能感兴趣的:(Android基础,android,java,开发语言)