Java对象序列化 Object Serialization

在最近的一次项目中,从服务器中请求得到了登录用户的相关信息,显然的,需要把其中的信息保存下来。在Android中,持久化保存信息的方式有很多种。比如数据库,首选项,又或者内部存储。但是,这个需要保存的信息字段有些多,目的是方便没网络时也能过显示一些相关的信息。我想到了对象序列化技术,把整个对象保存到磁盘中(内部存储)。
于是,结合网上的文章,及书本上的对应章节,把该知识点记录一下。
想要把一个对象能够被直接保存到数据库或磁盘中,有以下的步骤:

①实现Serializable接口,这是一个声明接口,没有需要实现的方法

    public class Person implements Serializable {
        public static final long serialVersionUID = 1L;
        private int age;
        private String password;
        //other fields

        //setter and getter
    }

②保存对象

    public void saveObject(Object obj) {
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;

        try {
            fos = new FileOutputStream("person.dat");
            oos = new ObjectOutputStream(fos);
            oos.writeObject(obj);

        } catch(Excepton e) {
            e.printStackTrace();
        } finally {
            //close stream
        }
    }

通过上面简单的几句代码,我们就把对象保存下来了。

③读取对象

    public Object readObject() {
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        Object obj = null;

        try {
            fis = new FileInputStream("person.dat");
            ois = new ObjectInputStream(fis);
            obj = ois.readObject();

        } catch(Exception e) {
            //...
        } finally {
            //close stream
        }

        return obj;
    }

通过上面的方法调用我们就把保存的对象从物理媒介中读取了出来

简单的序列化调用这样即可。当然,还需要理解关键字 transient的意思,它表示当序列化时,请把我忽略;同时,静态字段static也不会被序列化。

如果我们想保存某个对象时,需要修改,加密某些字段,应该要如果做?我们当然可以额外的在在保存时加密,取出来时再解密。但序列化机制提供了这样的方法截点。即,如果声明了Serializable的类中有实现private void writeObject(ObjectOutputStream oos);则保存时会调用该方法,而不是默认的调用ObjectOutputStream#defaultWriteObject()方法;如果实现了private void readObject(ObjectInputStream ois);方法,则读取时会调用该方法,而不是默认的ObjectInputStream#defaultReadObject()方法。当然推荐的是,当我们自己实现这两个方法时,首要的是调用默认的实现。我们举个例子,我们要对Person中的password字段加密后再保存,因为当对象保存到本地时,是可以轻易的读取出来的。
例子:

    public class Person implements Serializable {
        public static final long serialVersionUID = 1L;
        private String password;
        //other fields

        //other methods

        private void writeObject(ObjectOutputStream oos) {
            this.password = encrypt(this.password);
            try {
                oos.defaultWriteObject();
            } catch(Exception e) {}
        }


        private void readObject(ObjectInputStream ois) {
            try {
                ois.defaultReadObject();
                this.password = decrypt(this.password);

            } catch(Exception e) {}
        }

    }   

.
嗯,解决完这个问题。再来看一个序列化对象版本的问题。当读取一个序列号对象时,如果没有设置静态常量字段serialVersionUID,则虚拟机会根据类的相关信息动态生成一个新的版本号,这可能会导致类不兼容,引发InvalidClassException异常抛出。serialVersionUID的值可以自己随意设置,或者使用serialver工具生成,或者Eclipse中有智能提示,或者设置Android Studio自动生成。

再来看一个问题,假如我们这个对象的实例是Singleton单例模式,但是我们读取对象时是新建了一个对象,显然是不符合我们的需求的。所以,我们再看一下另一个方法private Object readResolve();这个方法也是写在声明了Serializable的类里边。该方法的特性是:

对于一个正在被反序列化的对象,如果它的类定义了一个readObject方法,并且具备正确的声明,那么在反序列化之后,新建对象上的readResolve方法就会被调用。然后,该放方法返回的对象引用将被返回,取代新对象。

所以,我们可以在该方法中返回单例对象。

.
另外,这里引申出一个简单的用法,即深度克隆一个对象:

    public Object cloneObject(Object srcObj) {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bout);
        out.writeObject(srcObj);
        out.close;

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream in = new ObjectInputStream(bin);
        Object ret = in.readObject();

        return ret;
    }

.

对于简单的序列化用法,有几点总结:
①显示声明序列化版本号serialVersionUID
②保护性地编写readObject方法,比如某些字段不能为null

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值