4

假设我正在为我的类定义一个自定义writeObjectreadObject用于序列化目的。该类有一个int在构造函数中初始化的最终属性()。期间writeObject没有任何问题。但是在读回对象时,我无法将值分配给属性,因为编译器抱怨我无法覆盖 final 属性并要求我从属性中删除 final 修饰符。有没有办法解决这个问题?

下面的课程可能会让您清楚地了解我正在努力实现的目标。 this.age = in.readInt();inreadObject()给我编译错误。

public class Person {

private String name = null;
private final int age;

public Person(String name, int age)
{
    this.name = name;
    this.age = age;
}

public void writeObject(ObjectOutputStream out) throws IOException
{
    out.writeObject(name);
    out.writeInt(age);
}

public void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
    this.name = (String) in.readObject();
    this.age = in.readInt();
}

}
4

3 回答 3

2

默认ObjectInputStream反序列化似乎用于sun.misc.Unsafe设置字段 ( java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(Object, Object[])),因此设置最终字段可能不是您想要做的事情。正如 Katona 在评论中建议的那样,您可以改为执行以下操作:

public class Person implements Serializable {

    private String name = null;

    private final int age;

    private int ageFromRead;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(name);
        out.writeInt(age);
    }

    private void readObject(ObjectInputStream in) throws IOException,
    ClassNotFoundException {
        this.name = (String) in.readObject();
        this.ageFromRead = in.readInt();
    }

    private Object readResolve() {
        return new Person(name, ageFromRead);
    }
}
于 2013-08-30T18:39:00.437 回答
1

readResolve 的问题是您需要暂时拥有实例中将被 readResolve 替换的所有状态。这与 final 不能很好地融合。

场景:您想将充满不受欢迎的可变性的东西拉入现代时代。但不会失去序列化形式的兼容性。并且最好不要从 readObject 到 readResolve 增长疯狂的桶连接临时状态。最好不要编写一个成熟的 SerializationProxy,因为它可能会被兼容性要求排除在外。

解决方案:将该临时状态捆绑在一个供应商闭包中:

public class AncientSerializableExample implements Serializable {
    // we have two fields in the example to illustrate the 
    // transient, because we don't want serializability defaults to interfere
    final public transient ImmutableList<Integer> immutableInts;
    final public transient ImmutableList<String> immutableStrings;

    /** funnels the data from readObject to readResolve, transient for obvious reasons,
     * we keep all the mutability reaquired to pass data to readResolve contained in here */
    private transient Supplier<AncientSerializableExample> deserializationResolver;

    public AncientSerializableExample(
            List<Integer> ints,
            List<String> strings
    ) {
        this.immutableInts = ImmutableList.copyOf(ints);
        this.immutableStrings = ImmutableList.copyOf(strings);
    }
    private void writeObject(final ObjectOutputStream out) throws IOException {
        // that ancient serializable form we want to keep untouched clearly wasn't using guava
        out.writeObject(new ArrayList<>(immutableInts));
        out.writeObject(new ArrayList<>(immutableStrings));
    }
    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
        List<Integer> freshlyReadInts = (List<Integer>) in.readObject();
        List<String> freshlyReadStrings = (List<String>) in.readObject();

        deserializationResolver = () -> { // our Supplier<AncientSerializableExample> captures the temporary state so conveniently!
            deserializationResolver = null; // don't keep the closure, it would prevent the deserialized ArrayLists from getting GCed
            return new AncientSerializableExample(
                    freshlyReadInts,
                    freshlyReadStrings
            );
        };
    }
    /** readResolve won't get any more complicated than this no matter how many fields reality throws at our class,
     * and the constructor call is nicely paired with the ObjectInputStream reading and no amount of formatting anarchy
     * thrown at the class can change that */
    private Object readResolve() throws ObjectStreamException {
        return deserializationResolver.get();
    }
}
于 2021-06-04T00:11:56.280 回答
0

我在这里找到了一个很好的示例,用于reflection设置final变量。

我将尝试将其转换为一个简单的示例:

public class SerializableClass implements Serializable {

    private final String finalVariable;

    /* Constructor and other methods */

    private void readObject(ObjectInputStream iStream) throws IOException, ClassNotFoundException {
    ObjectInputStream.GetField fields = iStream.readFields();


    try {
        Field id = this.getClass().getDeclaredField("finalVariable");

        // make finalVariable non "final"
        id.setAccessible(true);
        id.set(this, fields.get("finalVariable", null));

        // make field final again
        id.setAccessible(false);
    }
    catch (IllegalAccessException | NoSuchFieldException e) {
        System.out.println(e.getClass() + " : " + e.getMessage());
    }
}
于 2019-08-14T14:11:33.143 回答