2 yu766588220 yu766588220 于 2016.03.03 18:39 提问

麻烦帮忙看看这段关于java的序列化的代码,摘自Effective Java 7C
package org.effectivejava.examples.chapter11.item77;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Arrays;

// Broken singleton - has nontransient object reference field!
public class Elvis implements Serializable {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    private String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" };

    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }

    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
}
 package org.effectivejava.examples.chapter11.item77;
import java.io.Serializable;

public class ElvisStealer implements Serializable {
    static Elvis impersonator;
    private Elvis payload;

    private Object readResolve() {
        // Save a reference to the "unresolved" Elvis instance
        impersonator = payload;

        // Return an object of correct type for favorites field
        return new String[] { "A Fool Such as I" };
    }

    private static final long serialVersionUID = 0;
}
 package org.effectivejava.examples.chapter11.item77;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;

public class ElvisImpersonator {
    // Byte stream could not have come from real Elvis instance!
    private static final byte[] serializedForm = new byte[] { (byte) 0xac,
            (byte) 0xed, 0x00, 0x05, 0x73, 0x72, 0x00, 0x05, 0x45, 0x6c, 0x76,
            0x69, 0x73, (byte) 0x84, (byte) 0xe6, (byte) 0x93, 0x33,
            (byte) 0xc3, (byte) 0xf4, (byte) 0x8b, 0x32, 0x02, 0x00, 0x01,
            0x4c, 0x00, 0x0d, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65,
            0x53, 0x6f, 0x6e, 0x67, 0x73, 0x74, 0x00, 0x12, 0x4c, 0x6a, 0x61,
            0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x4f, 0x62, 0x6a,
            0x65, 0x63, 0x74, 0x3b, 0x78, 0x70, 0x73, 0x72, 0x00, 0x0c, 0x45,
            0x6c, 0x76, 0x69, 0x73, 0x53, 0x74, 0x65, 0x61, 0x6c, 0x65, 0x72,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
            0x4c, 0x00, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x74,
            0x00, 0x07, 0x4c, 0x45, 0x6c, 0x76, 0x69, 0x73, 0x3b, 0x78, 0x70,
            0x71, 0x00, 0x7e, 0x00, 0x02 };

    public static void main(String[] args) {
        // Initializes ElvisStealer.impersonator and returns
        // the real Elvis (which is Elvis.INSTANCE)
        Elvis elvis = (Elvis) deserialize(serializedForm);
        Elvis impersonator = ElvisStealer.impersonator;

        elvis.printFavorites();
        impersonator.printFavorites();
    }

    // Returns the object with the specified serialized form
    private static Object deserialize(byte[] sf) {
        Object result=null;
        try {
            InputStream is = new ByteArrayInputStream(sf);
            ObjectInputStream ois = new ObjectInputStream(is);
            result= ois.readObject();
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
        return result;
    }
}

这代码根本跑不起来,会报错。而ElvisStealer类的readResolve方法居然return new String[] { "A Fool Such as I" };更是让人摸不着头脑,如果返回的是ElvisStealer那还好,但是这里却返回字符数组,那不必错无疑么。求帮忙纠正下这段代码,然后讲一下ElvisStealer类的readResolve方法的return

3个回答

wojiushiwo945you
wojiushiwo945you   Ds   Rxr 2016.03.03 21:27

其次,这个readResolve方法的作用是让程序员定制反序列化操作返回的对象,反序列化调用readObject()时,底层是调用了readResolve()方法的。
最后,再分析你的main方法测试,给出的序列化字节serializedForm 是一个ElvisStealer类的对象的字节数组,它的两个属性payload都是有引用值的。所以你对这个字节数据反序列化时调用readObject时,先调用了ElvisStealer类的readResolve,然后又调用Elvis类的readResolve,得到了Elvis的实例,同时impersonator属性指向payload。

补充一下,而ElvisStealer类的readResolve方法,这里用return new String[] { "A Fool Such as I" },其实是为了定制序列化操作的返回值,通常默认的就是返回原来序列话的值,不会破坏原有的对象信息的。这里可能是为了显示定制,而返回特定的值。它与writeReplace序列化时写入的信息是一致的。我们在定制序列化时应该保证writeReplace和readResolve信息的一致。

wojiushiwo945you
wojiushiwo945you 回复ice-prince: 那就奇怪了。真不知道怎么回事了。
接近 2 年之前 回复
yu766588220
yu766588220 回复毕小宝: 我官方下载的JDK1.8,eclipse也是最新的,也在myeclipse跑过,给别人也试过,基本只有你能跑起来。其他都是报错。。。
接近 2 年之前 回复
wojiushiwo945you
wojiushiwo945you 回复ice-prince: 报找不到Elvis类的话,你重新clean下项目再试试呗。
接近 2 年之前 回复
wojiushiwo945you
wojiushiwo945you 回复ice-prince: 至于为什么会涉及到两个类对象的序列化呢,我也还没想明白。我猜测可能是你给的序列化字节数据中有类型信息,识别为ElvisStealer类了吧。
接近 2 年之前 回复
wojiushiwo945you
wojiushiwo945you 回复ice-prince: 为什么会报错呢?就是很简单的创建三个类,然后执行的啊。
接近 2 年之前 回复
yu766588220
yu766588220 我直接序列化ElvisStealer对象(不是序列化字节数组),然后再将 其反序列化,我这边是先调用Elvis的readResolve,再调用ElvisStealer的readResolve,跟你说的调用顺序不同。 而且这段代码我这边跑是会报Elvis的ClassNotFoundException。我用最新的mars版本的eclipse+jdk1.8。所以不太 明白在序列化字节的时候为什么是先调用ElvisStealer类的readResolve,而不是先调用Elvis类的readResolve
接近 2 年之前 回复
yu766588220
yu766588220 你好,很感谢你的回答。不过我还有点不明白。你说:“给出的序列化字节serializedForm 是一个ElvisStealer类的 对象的字节数组,它的两个属性payload都是有引用值的。所以对这个字节数据反序列化调用readObject时,先调 用了ElvisStealer类的readResolve,然后又调用Elvis类的readResolve”,这能再具体点么?为何会先调用 ElvisStealer类的readResolve。
接近 2 年之前 回复
caozhy
caozhy 赞,很详细
接近 2 年之前 回复
wojiushiwo945you
wojiushiwo945you   Ds   Rxr 2016.03.03 20:51

首先,我测试了你的代码,是能够运行的,我的环境是jdk1.8+Eclipse4.5,运行结果是:

[Hound Dog, Heartbreak Hotel]
[A Fool Such as I]
luozi007
luozi007 jdk1.7 + Kepler Service Release 2,为什么不能运行?
接近 2 年之前 回复
luozi007
luozi007   2016.03.03 23:30

楼上答得很好,我也将代码运行了一下,也报错。我用的是 jdk1.7 + Kepler Service Release 2。我查了一下,readResolve()方法与writeReplace()方法是对应的,readResolve()方法可以实现保护性复制整个对象,并且它会紧接着readObject()方法运行,该方法的返回值将会代替原来反序列化的对象
(即也就是readObject()的返回值,这个值将被丢弃)。常用于序列化单例类、枚举类。所以正如楼上所说,readResolve()和writeReplace()方法的作用是让程序员定制反序列化和序列化操作返回的对象

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!