java 不可序列化父类的可序列化子类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7662579/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 20:53:18  来源:igfitidea点击:

Serializable subclass of non-serializable parent class

javaandroidserialization

提问by jmc34

I am hitting a brickwall with serialization of a subclass of Location in android/java

我正在通过序列化 android/java 中的 Location 子类遇到砖墙

Location is not serializable. I have a first subclass called FALocation that does not have any instance variables. I have declared it serializable.

位置不可序列化。我有一个名为 FALocation 的第一个子类,它没有任何实例变量。我已经声明它是可序列化的。

Then I have a second class called Waypoint that looks like this:

然后我有一个名为 Waypoint 的第二个类,如下所示:

public class Waypoint extends FALocation implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /* Class variables *******************************************************/
    private static int CLASS_VERSION=1; //Used to version parcels

    /* Instance variables ****************************************************/
    private transient String type=DataHelper.PT_TYPE_US;
    private transient String country; 
    private transient String name=null;
    private transient String description=null;
    private transient int elevation = 0;
    private transient int population = 0; // Afterthought, added to match the DB structure

    /* Constructors **********************************************************/    
    public Waypoint() {
        super();
    }

    public Waypoint(double lat, double lon, String name, String description) {
        super(lat, lon);
        this.setName(name);
        this.setDescription(description);
    }

    public Waypoint(Location l) {
        super(l);
    }

    public Waypoint(String provider) {
        super(provider);
    }


    /* Implementing serializable */
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        Log.v("DroidFA", "Serialising \"%s\" (v%d).", Waypoint.class.getSimpleName(), CLASS_VERSION);
        out.writeInt(CLASS_VERSION);

        out.writeObject(type);
        out.writeObject(country);
        out.writeObject(name);
        out.writeObject(description);
        out.writeInt(elevation);
        out.writeInt(population);
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {

        int serialClassVersion = in.readInt();
        Log.v("DroidFA", "Deserialising \"%s\" (v%d).", Waypoint.class.getSimpleName(),serialClassVersion);

        type = (String) in.readObject();
        country = (String) in.readObject();
        name = (String) in.readObject();
        description = (String) in.readObject();
        elevation = in.readInt();
        population = in.readInt();
    }
}

Serialization works fine.

序列化工作正常。

Deseriamization produces the followwing exception (the leg object contains a waypoint).:

反序列化产生跟随异常(leg 对象包含一个航点):

10-05 13:50:35.259: WARN/System.err(7867): java.io.InvalidClassException: android.location.Location; IllegalAccessException
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.resolveConstructorClass(ObjectInputStream.java:2010)
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:2095)
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:929)
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2285)
10-05 13:50:35.278: WARN/System.err(7867):     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2240)
10-05 13:50:35.278: WARN/System.err(7867):     at com.droidfa.navigation.Leg.readObject(Leg.java:262)
.../...

回答by óscar López

Is it absolutely necessary to serialize the Location? maybe you could mark it as transient, and obtain it dynamically after deserializing the object. (Anyway, from the documentation ) :

是否绝对有必要对 Location 进行序列化?也许您可以将其标记为瞬态,并在反序列化对象后动态获取它。(无论如何,来自文档):

Q: If class A does not implement Serializable but a subclass B implements Serializable, will the fields of class A be serialized when B is serialized?

A: Only the fields of Serializable objects are written out and restored. The object may be restored only if it has a no-arg constructor that will initialize the fields of non-serializable supertypes. If the subclass has access to the state of the superclass it can implement writeObject and readObject to save and restore that state.

Q:如果类A没有实现Serializable但是子类B实现了Serializable,那么B序列化时A类的字段会被序列化吗?

A:只写出和恢复Serializable对象的字段。仅当对象具有将初始化不可序列化超类型的字段的无参数构造函数时,才可以恢复该对象。如果子类可以访问超类的状态,它可以实现 writeObject 和 readObject 来保存和恢复该状态。

So, if the subclass has access to the fields of its non-serializable superclass(es) it can use the writeObject and readObject protocol to implement serialization. Otherwise, there will be fields that won't be possible to serialize.

因此,如果子类可以访问其不可序列化超类的字段,则它可以使用 writeObject 和 readObject 协议来实现序列化。否则,将有无法序列化的字段。

回答by Mikko Maunu

Looks like Location does not have public/protected no-arg constructor. Such a constructor is needed for making it available for serialization in subclass.

看起来 Location 没有公共/受保护的无参数构造函数。需要这样的构造函数才能使其可用于子类中的序列化。

http://download.oracle.com/javase/6/docs/api/java/io/Serializable.htmlsays:

http://download.oracle.com/javase/6/docs/api/java/io/Serializable.html说:

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.

为了允许不可序列化的类的子类型被序列化,子类型可能负责保存和恢复超类型的公共、受保护和(如果可访问)包字段的状态。仅当它扩展的类具有可访问的无参数构造函数来初始化类的状态时,子类型才可能承担此责任。如果不是这种情况,则声明类 Serializable 是错误的。该错误将在运行时被检测到。

And same with the words from Serialization specification:

与序列化规范中的话相同:

A Serializable class must do the following: ... Have access to the no-arg constructor of its first nonserializable superclass

Serializable 类必须执行以下操作: ...有权访问其第一个不可序列化超类的无参数构造函数

That would explain why you have problems only in deserialization, because naturally constructor is not called during serialization.

这将解释为什么您只在反序列化中遇到问题,因为在序列化期间自然不会调用构造函数。

Small example of failing without accessible constructor:

没有可访问的构造函数失败的小例子:

public class A {
    public A(String some) {};
    private A() {} //as protected or public everything would work
}

public class B extends A implements Serializable {
    public B() {
        super("");
    }
    //these doesn't really matter
    //private void writeObject(java.io.ObjectOutputStream out) throws IOException {  }
    //private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { }
}

public class BSerializer {

    public static void main(String ... args) throws Exception {
        B b = new B();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(b);
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        B deserialized = (B) ois.readObject();   //InvalidClassException
    }
}