美文网首页
IPC-序列化

IPC-序列化

作者: 小的橘子 | 来源:发表于2019-03-08 23:31 被阅读0次

    Serializable

    序列化实现

    package com.nan.mylibrary;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    /**
     * 要序列化需要实现Serializable接口
     * Created by nan on 18-3-23.
     */
    
    public class Student implements Serializable {
        /**
        *可以不指定,可以由系统生成,也可手动指定,后面具体分析其使用及作用
        */
        private static final long serialVersionUID = -3042243308847924453L;
        public static String TAG;
        int age;
        String name;
        transient String nickname;
        boolean male;
    
        @Override
        public String toString() {
            return "Student{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", nickname='" + nickname + '\'' +
                    ", male=" + male +
                    '}' + " | Static TAG:" + TAG;
        }
    
        Student(int age, String name, String nickname, boolean male) {
            this.age = age;
            this.name = name;
            this.nickname = nickname;
            this.male = male;
    
        }
    
        public static void main(String[] args) {
            //1. 第一次运行main中注释的部分,直接输出studnet对象
            /*Student student = new Student(18, "dog", "feifei", false);
            Student.TAG = "hello student";
            System.out.println(student);
            serialize(student);*/
            //2.第二次运行反序列化代码部分,得到反序列化的数据并打印
            Student student1 = (Student) deserialize();
            System.out.println(student1);
    
        }
        /**
        *序列化对象
        */
        private static void serialize(Object object) {
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream("cache.txt");
                ObjectOutputStream ouput = new ObjectOutputStream(fileOutputStream);
                ouput.writeObject(object);
                ouput.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                System.out.println("file not found");
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IOException");
            }
        }
        /**
        *反序列化对象
        */
        private static Object deserialize() {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream("cache.txt");
                ObjectInputStream input = new ObjectInputStream(fileInputStream);
                Student student = (Student) input.readObject();
                return student;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    1. 正常打印student对象
    Student{age=18, name='dog', nickname='feifei', male=false} | Static TAG:hello student
    

    在当前对象中直接打印student对象可以看到所有信息都正常输出

    1. 反序列打印student对象
    Student{age=18, name='dog', nickname='null', male=false} | Static TAG:null
    

    由此可见序列化不会对static变量和transient关键字修饰成员进行序列化

    serialVersionUID


    反序列得到的对象只是在内容上和序列前对象内容一致,但本质是两个对象

    • 不指定serialVersionUID
      系统会在序列化时自动根据class类计算serialVersionUID,如果类有任何变化serialVersionUID就会发生变化,在反序列化时如果检测到前后序列码不一致则会反序列失败,报错如下
    java.io.InvalidClassException: com.nan.mylibrary.Student; local class incompatible: stream classdesc serialVersionUID = 8719351234228157191, local class serialVersionUID = -2675111980170094111
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1986)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
        at com.nan.mylibrary.Student.deserialize(Student.java:74)
        at com.nan.mylibrary.Student.main(Student.java:48)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    
    • IDE自动生成serialVersionUID
      类如何发生变化,则该ID也会发生变化,反序列化也会报错
    • 手动指明
      如果一些成员发生变化,系统会尽最大程度的恢复数据,但如果系统的发生重大变化(类名,类结构发生变化,此时发序列话也可能失败)。

    注意

    • 父类实现Serializable接口,子类被序列化,父类也会被序列化
    • 父类没有实现Serializable接口,子类被序列化,父类不会被序列化

    Parcelable

    实现了Parcelable接口的对象可以实现序列化并可以通过IntentBinder传递

    示例

    /**
    *step1 实现Parcelable接口
    */
    public class Student implements Parcelable {
    
        public static String TAG;
        String name;
        transient String nickname;
        int age;
        boolean male;
    
        public Student(String name, String nickname, int age, boolean male) {
            this.name = name;
            this.nickname = nickname;
            this.age = age;
            this.male = male;
    
        }
    
        /**
         * step5 读取数据必须和序列化中写数据顺序严格一致,否则可能值错乱,也有可能出错
         *
         * @param source
         */
        public Student(Parcel source) {
            this.name = source.readString();
            this.nickname = source.readString();
            this.age = source.readInt();
            this.male = source.readByte() == 1 ? true : false;
            TAG = source.readString();
        }
    
        /**
         * step2 必须实现,返回0即可
         * TODO:具体作用
         *
         * @return
         */
        @Override
        public int describeContents() {
            return 0;
        }
    
        //step3 将要序列化的内容写如Parcel
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeString(nickname);
            dest.writeInt(age);
            dest.writeByte((byte) (male ? 1 : 0));//writeboolean的特殊处理
            dest.writeString(TAG);
        }
    
        //step4 该CREATOR变量名称不能改变.
        public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
            @Override
            public Student createFromParcel(Parcel source) {
                //step6 从序列化的对象中创建原始对象
                return new Student(source);
            }
    
            @Override
            public Student[] newArray(int size) {
                return new Student[0];
            }
        };
    
        @Override
        public String toString() {
            return "Student{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", nickname='" + nickname + '\'' +
                    ", male=" + male +
                    '}' + " | Static TAG:" + TAG;
        }
    }
    

    测试该对象在两个不同进程的Activity传递,通过Intent来传递

    MainActivity 进程com.nan.ipc

    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void click(View view) {
            Student student = new Student("dog", "feifei", 18, true);
            Student.TAG = "hello student";
            Intent intent = new Intent(this, SecondActivity.class);
            //intent.putExtra接收为基本类型或者Parcelable和Serializable对象
            intent.putExtra("student", student);
            startActivity(intent);
        }
    }
    

    SecondActivity 进程com.nan.ipc:second

    public class SecondActivity extends Activity {
        public static final String TAG = "SecondActivity";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            Student student = getIntent().getParcelableExtra("student");
            Log.i(TAG, "onCreate: " + student);
        }
    }
    

    打印log如下

    01-01 15:43:14.202 10986 10986 I SecondActivity: onCreate: Student{age=18, name='dog', nickname='feifei', male=true} | Static TAG:hello student
    

    由上可知,写入Parcel中的数据都可以反序列出来,即使是transient修饰符修饰的变量或者static修饰的变量

    注意

    1. List和Map也可以序列化,前提是他们里面的每个元素都是可序列化的
    2. 如果序列化时包含已经序列化的对象,在读取是需要传入当前线程的上下文类加载器
    book = source.readParcelable(Thread.currentThread().getContentClassLoader());
    

    Serializable和Parcelable的区别

    • Parcelable和Serializable都是实现序列化并且都可以用于Intent间传递数据
    • Serializable是Java的实现方式,会频繁的IO操作,所以消耗比较大,但是实现方式简单。适用本地存储和网络传输。
    • Parcelable是Android提供的方式,其是将一个对象进行效率比较高,但是实现起来复杂一些 。

    二者的选取规则是:内存序列化上选择Parcelable, 存储到设备或者网络传输上选择Serializable(当然Parcelable也可以但是稍显复杂)

    相关文章

      网友评论

          本文标题:IPC-序列化

          本文链接:https://www.haomeiwen.com/subject/azrjpqtx.html