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币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!
其他相关推荐
Effective Java读书笔记八:序列化(74-78)
第13条:使类和成员的可访问性最小化 1:尽可能地使每个类或成员不被外界访问。换句话说,应该使用与你正在编写的软件的对应功能相一致、尽可能最小的访问级别。 2:对于顶层的(非嵌套的)类和接口,只有两种可能非访问级别,要么是包级私有的也就是default,要么是public的。包级私有就意味着它是这个包实现的一部分,不会作为该包API被导出,这样在以后的发型版本中,对它修改、替换、删除就不用担心
单例,枚举,反射,序列化--effectiveJava读书笔记
单例,枚举,反射,序列化
《Effective Java》中关于异常处理的几条建议
原文:http://www.cnblogs.com/skywang12345/p/3544168.html 概要 本章对Java中的异常进行介绍。内容包括: Java异常简介 Java异常框架 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3544168.html   Java异常简介   Java异常是
Effective Java之考虑用序列化代理代理序列化实例(七十八)
我们知道,实现了序列化的类。在反序列化时,实例的创建是由readObject方法来完成的。由于这是一个不同于构造函数的创建类实例的通道,因此在构造函数中的状态约束条件在readObjetc中也得一条不落下的实现。这很让人头大。 而且readObject的出现,让伪字节流攻击和内部域的盗用攻击成为可能。伪字节流攻击就是伪造一个字节流,通过readObject读取,内部域的盗用攻击是指用一个
《Effective Java》中文第二版源码
《Effective Java》中文第二版源码
Effective Java 英文第二版高清+源代码
JAVA四大名著之一,Joshua.Bloch经典著作; 只要 1 分,挥泪分享; 压缩包内文件: Effective Java第2版源代码.zip Effective Java Second Edition EN.pdf 仅供学习,严禁商业使用。 本书介绍了在Java编程中78条极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述,揭示了应该做什么,不应该做什么才能产生清晰、健壮和高效的代码。 本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。本书内容全面,结构清晰,讲解详细。可作为技术人员的参考用书。 ---------------------------------分割线---------------------------------- 其它三本名著介绍: 1.Think in java 中译:Java编程思想 2.The Java Programming Language 中译:Java编程语言或者Java程序设计语言 作者中有 James Gosling(Java之父) 3.Core Java 分为卷I 卷II Core Java, Volume I-Fundamentals 中译:Java核心技术 卷II 高级特性 Core Java, Volume II-Advanced Features 中译:Java核心技术 卷II 高级特性 早期译名《J2EE 核心技术》 -------------------------------------------------------------------------
为什么不能轻易的实现序列化(implements Serializable) 摘自《Effective Java》
为什么不能轻易的实现序列化(implements Serializable) 摘自《Effective Java》`第11章 序列化本章关注对象的序列化(object serialization)API,它提供了一个框架,用来将对象编码成字节流,以及从字节流编码中重新构建对象。”将一个对象编码成一个字节流”,这就称作序列化(serializing)该对象;相反的处理过程被称作反序列化(deseria
JAVA 泛型总结(结合JAVA核心技术和Effective Java两书)
一、基础知识 1、类型擦除 类型擦除指的是通过类型参数合并, 将泛型类型实例关联到同一份字节码上。 编译器只为泛型类型生成一份字节码, 并将其实例关联到这份字节码上, 因此泛型类型中的静态变量是所有实例共享的。 (1) 一个 static 方法, 无法访问泛型类的类型参数, 因为类还没有实例化, 所以, 若 static方法需要使用泛型能力, 必须使其成为泛型方法,(泛型参数稍后会
EffectiveJava 第二版示例源码
Effective Java 代码示例,阅读此书必备参考 创建和销毁对象 对于所有对象都通用的方法 类和接口 泛型 枚举和注解 方法 通用程序设计 异常 并发 序列化
BASE64编码问题
麻烦帮忙看看