zoukankan      html  css  js  c++  java
  • JAVA 对象序列化(三)——transient以及Externalizable的一种替代方法(使用Serializable)

         transient(瞬时)关键字

          当我们队序列化进行控制时,可能某个特定子对象不想让Java的序列化机制自动保存与恢复。如果子对象表示的是我们不希望将其序列化的敏感信息(如密码),通常就会面临这种情况。即使对象中的这些信息是private属性,一经序列化处理,人们就可以通过读取文件或者拦截网络传输的方式来访问它。

         在Java对象序列化(二)——Externalizable中我们通过将类实现为Externalizable可以实现类的部分序列化。本文提供了另一种可以实现部分序列化的方式,即通过关键字——transient(瞬时)。如果我们正在操作的是一个Serializable对象,那么所有序列化操作就会自动进行,为了能够给予控制,我们可以用transient关键字逐个字段的关闭序列化,它的意思“不烦恼您老保存或者恢复数据——我会自己处理的,谢谢。”

          由于Externalizable对象在默认情况下不保存它的任何字段(即任何字段都不进行序列化处理),所以transient关键字只能和Serializable对象一起使用。

        

         Externalizable的替代方法

           如果不是特别坚持实现Externalizable接口,那么我们可以通过实现Serializable接口,并添加名为writeObject()和readObject()方法(注意:这里用的是添加而不是“覆盖”或者“实现”,因为这两个方法不是基类Object也不是接口Serializable中的方法)。这样一旦对象被序列化或者反序列还原,就会自动地分别调用者两个方法。也就是说,只要我们提供了这两个方法,就会使用它们而不是默认的序列化机制。

            但是一定要注意,这两个方法的必须具有准确的方法特征签名,必须严格按照如下形式     

    private void writeObject(ObjectOutputStream stream) throws IOException{
    //TODO }
    private void readObject(ObjectInputStream stream) throws IOException , ClassNotFoundException{ //TODO }

          这两个方法看似简单,但是其实包含了一些比较有意思的处理。首先,这个方式不是Serializable接口或者基类中的一部分,所以必须在类内部自己实现。其次,注意到这个方式其实是private类型。也就是说这两个方法仅能被这个类的其他成员调用,但是我们看到,其实我们没有在这个类的其他的方法中调用这两个方法。那么到底是谁调用这两个方法呢?其实,实际上我们并没有从这个类的其他方法中调用它们,而是ObjectOutputStream和ObjectInputStream对象的writeObject和readObject()方法分别调用者两个方法(通过过反射机制来访问类的私有方法)。

           在调用ObjectOutputStream.writeObject()时,会检查所传递的Serializable对象,利用反射来搜索是否有writeObject()方法。如果有,就会跳过正常的序列化过程,转而调用这个它的writeObject()方法,readObject方法处理方式也一样。

           这里面有一个技巧,在类的writeObject()内部,可以通过ObjectOutputStream.defaultWriteObject()来执行默认的writeObject()(非transient字段由这个方法保存),同样的,在类readObject内部,可以通过ObjectInputStream.defalutReadObject()来执行默认的readObject()方法。

           下面这段代码就显示上述的技巧

    package test.serializable;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class SeriCtrol implements Serializable {
        private static final long serialVersionUID = -4994939941552821559L;
        
        private String a;
        
        private String b;
        
        private transient String c;
        
        public SeriCtrol(String a,String b,String c) {
            this.a = "非瞬时默认实现:" + a;
            this.b = "非瞬时非默认实现:"+ b;
            this.c = "瞬时实现:" + c;
        }
        
        private void writeObject(ObjectOutputStream stream) throws IOException{
            stream.defaultWriteObject();
            stream.writeObject(b);
            stream.writeObject(c);
        }
        
        private void readObject(ObjectInputStream stream) throws IOException , ClassNotFoundException{
            stream.defaultReadObject();
            stream.readObject();
            b= "null";
            c = (String)stream.readObject();
        }
        
        public String toString() {
            return a + b + c;
        }
        
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            SeriCtrol sCtrol = new SeriCtrol("test1","test2","test3");
            System.out.println("序列化之前");
            System.out.println(sCtrol);
            
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream oos  = new ObjectOutputStream(out);
            oos.writeObject(sCtrol);
            
            System.out.println("反序列化操作之后");
            ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(in);
            SeriCtrol src = (SeriCtrol) ois.readObject();
            System.out.println(src);
        }
    
    }

    运行结果如下:

    序列化之前
    非瞬时默认实现:test1非瞬时非默认实现:test2瞬时实现:test3
    反序列化操作之后
    非瞬时默认实现:test1null瞬时实现:test3

    从结果中可以看到,字段a没做什么处理,正常序列化,b在readObject()时,将其设置为null,相当于没有序列化了,而瞬时的c,我们可以手动的将其序列化。

          注意,个人在操作的过程发现,writeObject()和readObject()方法不需要成对出现,但是这时候readObject()要小心,如果没有writeObject()方法,在readObject()方法就不能再调用ObjectInputStream的readObject()方法。

           想想老刘在处理超链接废弃属性时处理,在readObject()方法中,将废弃的属性置为null.

      在“Thinking in Java”中提到如果想序列化static静态字段,必须自己手动去实现(p585),回头有时间,写个程序验证下。

  • 相关阅读:
    使用MFC界面库LibUIDK制作超酷界面
    使用WSH和WMI实现定时记录系统CPU和内存使用率
    关于CreateFileMapping的问题
    360对NSIS误报Malware.QVM06.GEN的解决办法
    win2003实现单用户远程登录
    All bytes must be within the maximum size specified by CreateFileMapping
    解决在VS2005中“当前不会命中断点。源代码与原始版本不同”的问题
    static library中能不能带资源
    HUDSON_HOME
    USB GUID查找
  • 原文地址:https://www.cnblogs.com/chenfei0801/p/3002453.html
Copyright © 2011-2022 走看看