一直以为反序列化就是把二进制流读进来组装成对象,不会涉及构造方法。然后写了如下代码:import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;public class TestSerializable { public static void main(String[] args) throws Exception {
Sub sub = new Sub("Adm", true);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.out"));
oos.writeObject(sub);

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.out"));
Sub other = (Sub)ois.readObject();
System.out.println(other);
}
}class Super {

private String name;

/*public Super() {
System.out.println("Damn it");
}*/

public Super(String name) {
this.name = name;
}

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

public String getName() {
return name;
}
}class Sub extends Super  implements Serializable {

private static final long serialVersionUID = 3846752112809249244L;

private boolean fuckable;

public Sub(String name, boolean fuckable) {
super(name);
this.fuckable = fuckable;
}

public void setFuckable(boolean fuckable) {
this.fuckable = fuckable;
}

public boolean getFuckable() {
return fuckable;
}

public String toString() {
return getName()+getFuckable();
}
}发现这段代码是会抛InvalidClassException的,把注释部分去掉就好了。总体来说觉得序列化与反序列化的过程是比较复杂的,涉及继承的时候更是如此:比如如果Super也实现了Serializable的话就没有异常。
2、还有一个问题:如果没有serialVersionUID,我将一个类的对象写入文件,然后修改这个类,最后会抛出异常,是因为违反了binary compatibility吗?

解决方案 »

  1.   

    过程是怎么样的我不清楚,有一点是通过反射创建一个类的对象,在这一点上,是需要一个无参的构造方法,然后调用相应的get/set方法吧.
      

  2.   

    serialVersionUID的作用
        当我们的类实现了Serializable接口后,会有一个警告,告诉你需要生成一个serialVersionUID属性。这个serialVersionUID是做什么用的呢?其实这是JAVA序列化的版本控制功能。当序列化对象时会把这个属性写入,当反序列化时则会把这个属性取出,然后与JAVA类中的serialVersionUID属性值对比,如果一致,则认为是同一个版本,正常反序列化,如果不一致则认为版本不同,抛出InvalidClassException异常。    很多时候我们忽略这个警告,并不写这个serialVersionUID属性,但仍然可以正常序列化。那是因为如果没有这个属性,JVM将会根据这个类的属性和方法,计算出一个值作为serialVersionUID的值。这种做法会带来潜在的风险。不同的JVM产生serialVersionUID的算法可能会不一致,如果在不同的环境下产生的serialVersionUID不一致,将导致反序列化失败!    当一个类的结构发生变化,需要改变serialVersionUID,以通知序列化机制此类发生了变化,不兼容原来的版本了!其实并不是只要类结构变化,就必须更改serialVersionUID,JAVA的序列化机制提供了部分变化的兼容机制,有如下几种:
    • 添加新的属性 反序列时发现没有此属性,则会赋予该属性默认的类型值
    • 添加 writeObject/readObject 方法 因为此方法是用于自定义序列化,不影响序列化
    • 删除 writeObject/readObject 方法 同上原因
    • 改变属性的访问权限(private protected package public)
    • 将一个属性从static变为nonstatic 或者 transient 或者 nontransient如果你的类改动属于以上范围,默认的JAVA序列化机制可以保证兼容性,也就是说你不需要改动serialVersionUID值。
    更多情况参见http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf 5.6.2 Compatible Changes :要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就 需要有默认的无参的构造函数。 在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取 父对象的变量值时,它的值是调用父类无参构造函数后的值。如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都 是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
      

  3.   

    "要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就 需要有默认的无参的构造函数。"这句话跟
    “ 在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的......”
    有矛盾,求解释.
      

  4.   

    6L解释很详细了。回8L,没有矛盾
    一个类要序列化,就要实现Serializable接口,反过来说,不实现Serializable就不能序列化
    当父类没实现Serializable接口,子类继承父类并实现Serializable接口,子类序列化时,父类的部分是没有被序列化的,那么反序列化的时候就没办法找到父类的部分,这时候怎么办呢,就调用父类构造器初始化父类部分。
    同样的道理,当一个类实现Serializable接口,其有些属性没实现Serializable接口,该类序列化时,属性也不会被序列化
      

  5.   

    javadoc里就有说明public interface Serializable类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。要允许不可序列化类的子类型序列化,可以假定该子类型负责保存和恢复超类型的公用 (public)、受保护的 (protected) 和(如果可访问)包 (package) 字段的状态。仅在子类型扩展的类有一个可访问的无参数构造方法来初始化该类的状态时,才可以假定子类型有此职责。如果不是这种情况,则声明一个类为可序列化类是错误的。该错误将在运行时检测到。在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数构造方法。可序列化子类的字段将从该流中恢复。 当遍历一个图形时,可能会遇到不支持 Serializable 接口的对象。在此情况下,将抛出 NotSerializableException,并将标识不可序列化对象的类。 
      

  6.   

    上面是中文版的javadoc,英文版public interface SerializableSerializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable. To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime. During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream. When traversing a graph, an object may be encountered that does not support the Serializable interface. In this case the NotSerializableException will be thrown and will identify the class of the non-serializable object. 
      

  7.   


    不是调set,而是用反射直接写(field)属性值,因为序列化的对象不一定是bean。