java基础序列化

139 阅读4分钟

一、概念

序列化:把数据结构或对象转换成二进制串的过程。

反序列化:把二进制串转化成数据结构和对象的过程。

持久化:把数据结构或对象存储起来。(先序列化再保存)

序列化的方案:serializable、parcelable、json、xml等。

二、序列化接口

1.java接口

Serializable 和 Externalizable

image.png

image.png

2. 序列化方法(Serializable)

 synchronized public static boolean saveObject(Object obj, String path) {//持久化
        if (obj == null) {
            return false;
        }
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(path));// 创建序列化流对象
            oos.writeObject(obj);
            oos.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close(); // 释放资源
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

3.反序列化(Serializable)

@SuppressWarnings("unchecked ")
    synchronized public static <T> T readObject(String path) {
        ObjectInputStream ojs = null;
        try {
            ojs = new ObjectInputStream(new FileInputStream(path));// 创建反序列化对象
            return (T) ojs.readObject();// 还原对象
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ojs!=null){
                try {
                    ojs.close();// 释放资源
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

4.使用Externalizable的注意事项

  • 实现的两个方法,需要按顺序的调用对象的写入和读出。
 @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(name);
            out.writeInt(age);
        }

        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
          name = (String)in.readObject();
          age = in.readInt();
        }
  • 在有重载构造方法的时候一定要加上无参的构造方法。

三、序列化的几个面试题

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

serialVersionUID:是一个 private static final long 型 ID当它被印在对象上时, 它通常是对象的哈希码,他的作用是用于对象的版本控制。

private static final long serialVersionUID = 2;//可以自己代码控制有则会覆盖默认值。

2.你希望某些成员不要序列化?怎么实现?

transient 去修时相应的变量。

3. 可序列化对象包含没有实现serializable接口的对象。会出现什么问题?

在运行时将引发不可序列化异常 NotSerializableException

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

父类的成员变量都是初始值。

如果想要序列化父类而不让父类实现序列化的接口

private void writeObject(ObjectOutputStream out) throws IOException {//不是重写父类的方案
            out.defaultWriteObject();
            out.writeObject(getSex());
            out.writeInt(getId());
        }

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

5. 父类序列化了,子类继承父类,子类默认也是可以序列化的。

6.父类序列化了,如何让子类不能进行序列化。


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

        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
            throw new NotSerializableException("Can not serialize this class");
        }

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

这里的私有方法在序列化的时候是通过反射来判断这几个方法是否存在。

image.png

7.java中的序列化真正进行序列化的是objectOutputStream和objectInputStream。java的序列化是靠流操作来实现的。

四、Android的parcelable

parcelable是通过调用native方法来实现序列化的。直接在内存操作。通过binder进行跨进程传输。

image.png

五、面试题

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

不会。

实例化要靠 ConstructorAccessor 实例。
而此时该对象实MethodAccessorGenerator#generateSerializationConstructor 方法中通过 asm 操作字节码返回的全新
SerializationConstructorAccessorImpl 对象,通过这个对象就可以创建我们想要的 User 对象了。但是这种方式反序列化
创建对象时,是不会调用我们源代码 User 中的默认构造函数的。

2.序列前的对象与序列化后的对象是什么关系?是("=="还是equal?是浅复制还是深复制?)//枚举

是两个对象,属于深拷贝,地址不同。(枚举除外)

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

Bundle里使用的是ArrayMap。在空间节省上优于hashMap.

4. SerialVersionID的作用是什么?

版本控制,当前后对象这个值不一样的时候,会报错误提示。

5. Android中Intent/Bundle的通信原理及大小限制

binder快进程通信。因为受binder通信的限制,所以最大是1M - 8KB的。

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

创建组件是需要跨进程通信,通过AMS的。跨进程通信就需要把对象数据序列化才能进行传输。

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

序列化是为了跨进程通讯。

持久化是为了把数据结构或者对象保存下来。