Java中ArrayList的序列化,既然重写了writeObject和readObject,为什么还要用transient来修饰elementData?

小栋的滑板鞋 发布于 2017/02/22 14:30
阅读 1K+
收藏 0

Transient: 此关键字智能用于变量,表示不能被序列化。

在ArrayList中存储元素的变量用transient修饰,所以理论上ArrayList不能被序列化。但是在ArrayList中却可以,因为在ArrayList中重写了writeObject 这个方法,所以可以转化为文件流,可以被序列化。

网上说这样设计的目的是因为在ArrayList中的存放元素的是一个素组,这个数组的容量大小基本上都会比实际的元素的个数要大,为了避免序列化没有元素的数组而重写。

实际上在我的测试中确实也是如此,阅读ObjectOutInputStream的源码会发现,如果你重写了writeObject 和readObjec 这个两个方法,在实际序列化的时候,会利用反射最终调用到你重写的writeObject和readObject 来序列化。

下面是例子,首先创建了一个类

package com.zyd.serializable.test;

import java.io.IOException;
import java.io.Serializable;

public class AnimalSerializable implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -7461498806905104482L;
    
    public AnimalSerializable() {
        
    }
    
    public AnimalSerializable(int age, String name){
        this.age = age;
        this.name = name;
    }
    
    private  int age ;
    private  String name;
    
      private void writeObject(java.io.ObjectOutputStream s){
          try {
            s.writeObject(name);
            s.writeObject(age);
            System.out.println("重写了writeObject");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
      }
      private void readObject(java.io.ObjectInputStream s){
          try {
            name = (String) s.readObject();
            age = (int) s.readObject();
            System.out.println("重写了 readObject");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
      }
      
      
    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "AnimalSerializable [age=" + age + ", name=" + name + "]";
    }
    
    

}
 

然后测试:

public static void main(String[] args) {
        AnimalSerializable animal = new AnimalSerializable(10, "xiaohong");
        
        File file = new File("D:/test.txt");
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        try {
            ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(file));
            output.writeObject(animal);
            
            ObjectInputStream input = new ObjectInputStream(new FileInputStream(file));
            AnimalSerializable anima = (AnimalSerializable) input.readObject();
            System.out.println(anima.toString());
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

最后的输出结果是

重写了writeObject
重写了 readObject
AnimalSerializable [age=10, name=xiaohong]

 

但是 我就有一点不明白了,在上面的例子中我并没有用到transient,只要重写writeObject就可以实现序列化,既然重写writeObject 和readObject 就可以自己序列化,那么arrayList中用到transient岂不是很多余?

加载中
0
如比如比
如比如比

这里有个资料http://www.hollischuang.com/archives/1140

小栋的滑板鞋
小栋的滑板鞋
谢谢,好像明白了,应该是在writeObject中调用了s.defaultWriteObject()这个方法,这个方法是用来序列化那些没有transient的变量,但是那些加了transient的就需要自己来实现了。Thanks
0
kakai
kakai

很多序列化类库是不会修改java自身类的writeObject和readObject方法的,如果你要自己实现对象序列化和反序列化List对象,自己遍历写入到字节缓冲区就行了,不应该依靠java自身的writeObject和readObject方法

kakai
kakai
回复 @小栋的滑板鞋 : 你可以看看kryo或其它序列化类库的源码,相信对你会有所帮助的
小栋的滑板鞋
小栋的滑板鞋
不懂,能否给个例子?
0
ihotman
ihotman

刚毕业??看了资料都不懂,理解力我也是服了

返回顶部
顶部