深入理解JAVA序列化

一.对象的序列化及反序列化

java对象在序列化及反序列化之前,必须实现 Serializable 接口;

package com.river.entity;

import java.io.Serializable;

/**
 * Created by on 2017/8/15.
 */
public class Person implements Serializable{

    private static final long serialVersionUID = 1L;

    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

1.序列化

@Test
    public void test1() throws Exception{
        Person p = new Person("xiaoli",12);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.obj"));
        out.writeObject(p);

    }

结果:

165003_G8TD_3220575.png

2.反序列化

    @Test
    public void test2() throws Exception{
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.obj"));
        Person person = (Person) in.readObject();
        System.out.println(person);

    }

结果略;

二.场景分析

1.若Person类未实现序列化接口:

java.io.NotSerializableException: com.river.entity.Person

2.serialVersionUID的作用

    serialVersionUID的作用是用来区分序列化前后的类是否一致,若serialVersionUID的值不一致,则序列化失败;

如下序列化前该值为1L,反序列化前将该值改为2L;

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

3.序列化对象有父类

1)当父类未实现序列化接口时

    @Test
    public void test1() throws Exception{
        Person p = new Person("xiaoli",12);
        p.setCreateTime(new Date());
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.obj"));
        out.writeObject(p);

    }
    @Test
    public void test2() throws Exception{
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.obj"));
        Person person = (Person) in.readObject();
        System.out.println(person.getCreateTime());

    }

运行结果:

null

可以看到,父类中的属性值是没有被反序列化回来的;

分析:

       要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就需要有默认的无参的构造函数。 在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取 父对象的变量值时,它的值是调用父类无参构造函数后的值。如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都 是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

4.某些字段不想序列化  

      Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

       若有大量的属性不序列化,可以考虑利用场景3的情况将它们统一封装在父类中;

5.序列化前自定义处理

        例如对象中有用户名密码字段,序列化前加密处理,如何操作呢?

        对象中自定义相同签名的序列化方法:

package com.river.entity;

import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * Created by on 2017/8/15.
 */
public class Person extends BaseEntity implements Serializable{

    private static final long serialVersionUID = 1L;

    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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


    private void writeObject(ObjectOutputStream in){
        System.out.println(1);
    }
}

运行序列化方法:

1

可见调用了自定义的方法,我们就可以自定义实现了;

同理:

对象中自定义相同签名反序列化的方法,并加上解密逻辑即可实现反序列化对加密字段进行解密;

三.序列化与单例

    单例模式可以被反射破坏,除了反射,序列化与反序列化也会破坏单例;

public class Singleton implements Serializable{
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Write Obj to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(Singleton.getSingleton());
        //Read Obj from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        //判断是否是同一个对象
        System.out.println(newInstance == Singleton.getSingleton());
    }

运行结果为: false ,说明单例模式与反序列化得到的对象不是同一个对象,反序列化出一个新的对象,破坏的单例性;

                  未命名文件 (1)

    private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {
            throw new InternalError();
        }

        ObjectStreamClass desc = readClassDesc(false);
        desc.checkDeserialize();

        Class<?> cl = desc.forClass();
        if (cl == String.class || cl == Class.class
                || cl == ObjectStreamClass.class) {
            throw new InvalidClassException("invalid class descriptor");
        }

        Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null;
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }

        passHandle = handles.assign(unshared ? unsharedMarker : obj);
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(passHandle, resolveEx);
        }

        if (desc.isExternalizable()) {
            readExternalData((Externalizable) obj, desc);
        } else {
            readSerialData(obj, desc);
        }

        handles.finish(passHandle);

        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
        {
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                handles.setObject(passHandle, obj = rep);
            }
        }

        return obj;
    }

     这段代码:     

obj = desc.isInstantiable() ? desc.newInstance() : null;
boolean isInstantiable() {
        requireInitialized();
        return (cons != null);
    }
private final void requireInitialized() {
        if (!initialized)
            throw new InternalError("Unexpected call when not initialized");
    }

该类构造方法:

 private ObjectStreamClass(final Class<?> cl) {
        this.cl = cl;
        name = cl.getName();
        isProxy = Proxy.isProxyClass(cl);
        isEnum = Enum.class.isAssignableFrom(cl);
        serializable = Serializable.class.isAssignableFrom(cl);
        externalizable = Externalizable.class.isAssignableFrom(cl);

        Class<?> superCl = cl.getSuperclass();
        superDesc = (superCl != null) ? lookup(superCl, false) : null;
        localDesc = this;

        if (serializable) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    if (isEnum) {
                        suid = Long.valueOf(0);
                        fields = NO_FIELDS;
                        return null;
                    }
                    if (cl.isArray()) {
                        fields = NO_FIELDS;
                        return null;
                    }

                    suid = getDeclaredSUID(cl);
                    try {
                        fields = getSerialFields(cl);
                        computeFieldOffsets();
                    } catch (InvalidClassException e) {
                        serializeEx = deserializeEx =
                            new ExceptionInfo(e.classname, e.getMessage());
                        fields = NO_FIELDS;
                    }

                    if (externalizable) {
                        cons = getExternalizableConstructor(cl);
                    } else {
                        cons = getSerializableConstructor(cl);
                        writeObjectMethod = getPrivateMethod(cl, "writeObject",
                            new Class<?>[] { ObjectOutputStream.class },
                            Void.TYPE);
                        readObjectMethod = getPrivateMethod(cl, "readObject",
                            new Class<?>[] { ObjectInputStream.class },
                            Void.TYPE);
                        readObjectNoDataMethod = getPrivateMethod(
                            cl, "readObjectNoData", null, Void.TYPE);
                        hasWriteObjectData = (writeObjectMethod != null);
                    }
                    writeReplaceMethod = getInheritableMethod(
                        cl, "writeReplace", null, Object.class);
                    readResolveMethod = getInheritableMethod(
                        cl, "readResolve", null, Object.class);
                    return null;
                }
            });
        } else {
            suid = Long.valueOf(0);
            fields = NO_FIELDS;
        }

        try {
            fieldRefl = getReflector(fields, this);
        } catch (InvalidClassException ex) {
            // field mismatches impossible when matching local fields vs. self
            throw new InternalError(ex);
        }

        if (deserializeEx == null) {
            if (isEnum) {
                deserializeEx = new ExceptionInfo(name, "enum type");
            } else if (cons == null) {
                deserializeEx = new ExceptionInfo(name, "no valid constructor");
            }
        }
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getField() == null) {
                defaultSerializeEx = new ExceptionInfo(
                    name, "unmatched serializable field(s) declared");
            }
        }
        initialized = true;
    }

 

initialized = true;

可知:

isInstantiable:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。针对serializable和externalizable我会在其他文章中介绍。

desc.newInstance:该方法通过反射的方式调用无参构造方法新建一个对象。

序列化会通过反射调用无参数的构造方法创建一个新的对象。

如何防止序列化反序列化破坏单例呢?

        利用序列化对象内自定义反序列方法,

 private Object readResolve() {
        return singleton;
    }
if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
        {
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                handles.setObject(passHandle, obj = rep);
            }
        }

这样的化反序列化得到的就是单例返回的实例,而不用通过无参构造创建新对象了,避免了破坏单例;

 

转载于:https://my.oschina.net/JackieRiver/blog/1787028

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值