在反序列化期间回读自定义readObject中的最终值?

时间:2013-08-30 17:32:28

标签: java serialization

让我们说我正在为我的类定义一个自定义的writeObject和readObject以进行序列化。该类有一个final属性(int),它在构造函数中初始化。在writeObject期间没有问题。但是在回读对象时,我无法将值赋给属性,因为编译器抱怨我无法覆盖最终属性并要求我从属性中删除最终修饰符。有没有办法解决这个问题?

下课可能会让你清楚地了解我想要实现的目标。 “this.age = in.readInt();”在readObject()中给出了编译错误。

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();
}

}

3 个答案:

答案 0 :(得分:2)

默认的ObjectInputStream反序列化似乎使用sun.misc.Unsafe来设置字段(java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(Object, Object[])),因此设置最终字段可能不是您想要做的事情。正如卡托纳在评论中建议的那样,您可以做一些类似的事情:

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);
    }
}

答案 1 :(得分:1)

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

场景:您想将充满不希望的可变性的东西拉入现代。但不会失去序列化形式的兼容性。并且最好不要从 readObject 到 readResolve 增长疯狂的bucket-brigading临时状态。最好不要编写一个完整的 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();
    }
}

答案 2 :(得分:0)

我发现了一个很棒的示例here,该示例使用Exception Type: EXC_CRASH (SIGKILL) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: Namespace SPRINGBOARD, Code 0xdead10cc Termination Description: SPRINGBOARD, com.xxx.app was task-suspended with locked system files: | /var/mobile/Containers/Shared/AppGroup/9EAF384F-B017-4E91-BA24-1B9192E31220/database.db | ProcessVisibility: Background | ProcessState: Suspended Triggered by Thread: 0 Thread 0 Crashed: 0 libsystem_kernel.dylib 0x000000021c3b9c60 mach_msg_trap + 8 1 CoreFoundation 0x000000021c7c1e10 __CFRunLoopServiceMachPort + 240 (CFRunLoop.c:2615) 2 CoreFoundation 0x000000021c7bcab4 __CFRunLoopRun + 1344 (CFRunLoop.c:2971) 3 CoreFoundation 0x000000021c7bc254 CFRunLoopRunSpecific + 452 (CFRunLoop.c:3247) 4 GraphicsServices 0x000000021e9fbd8c GSEventRunModal + 108 (GSEvent.c:2245) 5 UIKitCore 0x0000000249b044c0 UIApplicationMain + 216 (UIApplication.m:4353) 6 Gear 0x0000000102591a94 main + 68 (BaseTabBarController.swift:14) 7 libdyld.dylib 0x000000021c278fd8 start + 4 Thread 1 name: Thread 1: 0 libsystem_kernel.dylib 0x000000021c3b9c60 mach_msg_trap + 8 1 CoreFoundation 0x000000021c7c1e10 __CFRunLoopServiceMachPort + 240 (CFRunLoop.c:2615) 2 CoreFoundation 0x000000021c7bcab4 __CFRunLoopRun + 1344 (CFRunLoop.c:2971) 3 CoreFoundation 0x000000021c7bc254 CFRunLoopRunSpecific + 452 (CFRunLoop.c:3247) 4 Foundation 0x000000021d19c04c -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 304 (NSRunLoop.m:367) 5 Foundation 0x000000021d19bed4 -[NSRunLoop(NSRunLoop) runUntilDate:] + 100 (NSRunLoop.m:411) 6 UIKitCore 0x0000000249bf00d4 -[UIEventFetcher threadMain] + 140 (UIEventFetcher.m:606) 7 Foundation 0x000000021d19ac4c -[NSThread main] + 72 (NSThread.m:1212) 8 Foundation 0x000000021d2d0e54 __NSThread__start__ + 988 (NSThread.m:1175) 9 libsystem_pthread.dylib 0x000000021c43a908 _pthread_body + 132 (pthread.c:857) 10 libsystem_pthread.dylib 0x000000021c43a864 _pthread_start + 48 (pthread.c:884) 11 libsystem_pthread.dylib 0x000000021c442dcc thread_start + 4 Thread 2 name: Thread 2: 0 libsystem_kernel.dylib 0x000000021c3c4ed4 __psynch_cvwait + 8 1 libc++.1.dylib 0x000000021b9cc990 std::__1::condition_variable::wait(std::__1::unique_lock<std::__1::mutex>&) + 28 (__threading_support:278) 2 JavaScriptCore 0x0000000223ae550c void std::__1::condition_variable_any::wait<std::__1::unique_lock<bmalloc::Mutex> >(std::__1::uni... + 104 (condition_variable:204) 3 JavaScriptCore 0x0000000223ae9560 bmalloc::Scavenger::threadRunLoop() + 172 (condition_variable:213) 4 JavaScriptCore 0x0000000223ae8cfc bmalloc::Scavenger::threadEntryPoint(bmalloc::Scavenger*) + 16 (Scavenger.cpp:359) 5 JavaScriptCore 0x0000000223aea724 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, st... + 44 (type_traits:4345) 6 libsystem_pthread.dylib 0x000000021c43a908 _pthread_body + 132 (pthread.c:857) 7 libsystem_pthread.dylib 0x000000021c43a864 _pthread_start + 48 (pthread.c:884) 8 libsystem_pthread.dylib 0x000000021c442dcc thread_start + 4 Thread 3 name: Thread 3: 0 libsystem_kernel.dylib 0x000000021c3b9c60 mach_msg_trap + 8 1 CoreFoundation 0x000000021c7c1e10 __CFRunLoopServiceMachPort + 240 (CFRunLoop.c:2615) 2 CoreFoundation 0x000000021c7bcab4 __CFRunLoopRun + 1344 (CFRunLoop.c:2971) 3 CoreFoundation 0x000000021c7bc254 CFRunLoopRunSpecific + 452 (CFRunLoop.c:3247) 4 CFNetwork 0x000000021cdddc88 -[__CoreSchedulingSetRunnable runForever] + 208 (CoreSchedulingSet.mm:1365) 5 Foundation 0x000000021d19ac4c -[NSThread main] + 72 (NSThread.m:1212) 6 Foundation 0x000000021d2d0e54 __NSThread__start__ + 988 (NSThread.m:1175) 7 libsystem_pthread.dylib 0x000000021c43a908 _pthread_body + 132 (pthread.c:857) 8 libsystem_pthread.dylib 0x000000021c43a864 _pthread_start + 48 (pthread.c:884) 9 libsystem_pthread.dylib 0x000000021c442dcc thread_start + 4 Thread 4: 0 libsystem_pthread.dylib 0x000000021c442dc0 start_wqthread + 0 (pthread_dependency.c:103) Thread 5: 0 libsystem_pthread.dylib 0x000000021c442dc0 start_wqthread + 0 (pthread_dependency.c:103) Thread 0 crashed with ARM Thread State (64-bit): x0: 0x0000000010004005 x1: 0x0000000007000806 x2: 0x0000000000000000 x3: 0x0000000000000c00 x4: 0x0000000000002a03 x5: 0x00000000ffffffff x6: 0x0000000000000000 x7: 0x0000000000000403 x8: 0x00000000fffffbbf x9: 0x0000000007000000 x10: 0x0000000007000000 x11: 0x000000b2f51efc80 x12: 0x00000000016e3600 x13: 0x000000000003757e x14: 0x000000000004b400 x15: 0x0004b40000000000 x16: 0xffffffffffffffe1 x17: 0x0000000000000001 x18: 0x0000000000000000 x19: 0x0000000000000000 x20: 0x00000000ffffffff x21: 0x0000000000002a03 x22: 0x0000000000000c00 x23: 0x000000016d872d30 x24: 0x0000000007000806 x25: 0x0000000000000000 x26: 0x0000000007000806 x27: 0x0000000000000c00 x28: 0x0000000000000001 fp: 0x000000016d872c20 lr: 0x000000021c3b90e8 sp: 0x000000016d872bd0 pc: 0x000000021c3b9c60 cpsr: 0x60000000 来设置reflection变量。

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

final