首页

源码搜藏网

首页 > 开发教程 > 软件工程 >

java 序列化机制深度解析

创建时间:2016-06-17 16:41  

概要

不被序列化的内容

可序列化的类型

public class Demo {
    public static void main(String[] args) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempFile"));
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile"));
        out.writeObject("hello world");
        out.writeObject(new int[]{1, 2, 3});
        out.writeObject(Color.BLACK);
        out.writeObject(new Data(1, "one"));
        out.writeObject(new Integer(1));
        System.out.println(in.readObject());
        System.out.println(Arrays.toString((int[]) (in.readObject())));
        System.out.println((Color) in.readObject());
        Data data = (Data) in.readObject();
        System.out.println(data);
        System.out.println(in.readObject());
    }
}
enum Color {
    BLACK, RED
}
class Data implements Serializable {
    int x;
    String s;
    public Data(int x, String s) {
        this.x = x;
        this.s = s;
    }
    @Override
    public String toString() {
        return x + "    " + s;
    }
}
/* 结果:hello world
[1, 2, 3]
BLACK
1    one
1 
*/

递归序列化

public class Demo {
    public static void main(String[] args) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempFile"));
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile"));
        out.writeObject(new Data(1, "one"));
        Data data = (Data) in.readObject();
        System.out.println(data);
    }
}  
class Data implements Serializable {
    int x;
    String s;
    SubData subdata = new SubData(1);
    public Data(int x, String s) {
        this.x = x;
        this.s = s;
    }
    @Override
    public String toString() {
        return x + "    " + s + subdata.x;
    }
}
class SubData {
    int x;
    public SubData(int x) {
        this.x = x;
    }
}  

定制序列化

在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。

实例 ArrayList的序列化

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();
        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);
        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }  
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;
        // Read in size, and any hidden stuff
        s.defaultReadObject();
        // Read in capacity
        s.readInt(); // ignored
        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);
            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }  

替换序列化对象

wirteReplace():只要该方法存在,由序列化机制调用,在序列化对象时将该对象替换成其它对象;


public class Person4 implements Serializable {
    private String name;
    private int age;
    public Person4(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    private Object writeReplace() throws ObjectStreamException {
        ArrayList<Object> list = new ArrayList<Object>();
        list.add(name);
        list.add(age);
        return list;
    }
    public static void main(String[] args) {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("replace.txt"));
            ObjectInputStream ooi = new ObjectInputStream(new FileInputStream("replace.txt"));
            Person4 per = new Person4("孙悟空", 500);
            // 系统将per对象转换成字节序列并输出
            oos.writeObject(per);
            // 反序列化读取得到的是ArrayList
            ArrayList list = (ArrayList) ooi.readObject();
            System.out.println(list.toString());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}  

序列化机制深入分析

            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }  
if (slotDesc.hasWriteObjectMethod()) {
            ...
                    slotDesc.invokeWriteObject(obj, this);  

serialVersonUID

序列化版本兼容

反序列化Java对象时,必须提供该对象的class文件,随着项目的升级,系统的class文件也会升级。如果保持两个class文件兼容性:
- Java的序列化机制允许为序列化类提供一个private static final的serialVersonUID值,该Field值用于标识该Java类的序列化版本;
- 最好在每个要序列化的类中加入private static final long serialVersionUID这个Field,具体数值自己定义。这样,即使对象被序列化之后,它所对应的类修改了,该对象也依然可以被正确反序列化;

如果不显示定义serialVersionUID值:
- 该Field值将由JVM根据类的相关信息计算,而修改后的类计算结果与修改前的类计算结果往往不同,从而造成对象的反序列化因为类版本的不兼容失败;
- 不利于程序在不同的JVM之间移植,因为不同的编译器计算该Field的值计算策略可能不同,从而造成虽然类完全没有改变,但是因为JVM不同,也会出现序列化版本不兼容而无法正确反序列化的现象;

多次序列化同一对象

- 当程序视图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该对象从未(在本次虚拟机)中序列化过,系统才会将该对象转换成字节序列并输出; 
- 如果某个对象已经序列化过,程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象,因此当重复序列化同一个对象时即便变量域已经改变,反序列化时还是只会保存第一个序列化对象的状态
```java
public class SerializaMutable {
    public static void main(String[] args) {
        try {
            // 创建一个ObjectOutputStream对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("mutable.txt"));
            // 创建一个ObjectInputStream对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("mutable.txt"));
            Person per = new Person("孙悟空", 500);
            // 系统将per对象转换成字节序列并输出
            oos.writeObject(per);
            per.setName("猪八戒");
            // 系统只是输出序列化编号,所以改变后的name不会被序列化
            oos.writeObject(per);
            Person p1 = (Person) ois.readObject();
            Person p2 = (Person) ois.readObject();
            // 下面输出true,即反序列化后p1等于p2
            System.out.println(p1 == p2);
            // 下面依然看到输出“孙悟空”,即改变后的Field没有被序列化
            System.out.println(p2.getName());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class Person implements Serializable {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
} 
/*
结果:
true
孙悟空  
*/ 




<div class="se-preview-section-delimiter"></div>

Externalizable 序列化机制

public class Person5 implements Externalizable {
    private String name;
    private int age;
    // 必须提供无参数的构造函数,否则报java.io.InvalidClassException
    public Person5() {
        super();
    }
    // 注意此处没有提供无参数的构造器
    public Person5(String name, int age) {
        super();
        System.out.println("有参数的构造器");
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        // 将去读的字符串反转后赋值给name Field
        this.name = ((StringBuffer) in.readObject()).reverse().toString();
        this.age = in.readInt();
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // 将name Field值反转后写入二进制流
        out.writeObject(new StringBuffer(name).reverse());
        out.writeInt(age);
    }
    public static void main(String[] args) {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("external.txt"));
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("external.txt"));
            Person5 per = new Person5("孙悟空", 500);
            oos.writeObject(per);
            Person5 p = (Person5) ois.readObject();
            System.out.println(p.getName() + ":" + p.getAge());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}  

序列化机制对比

0
0
   
上一篇:高斯消元整数消元模板
下一篇:Lua的table库函数insert、remove、concat、sort详细介绍

相关内容

热门推荐