Object  readResolve()

这个方法会紧挨着readObject()之后被调用,该方法的返回值将会代替原来反序列化的对象,而原来readObject()反序列化的对象将会立即丢弃。

readObject()方法在序列化单例类时尤其有用。当然,如果使用java5提供的enum来定义枚举类,则完全不用担心,程序没有任何问题。

我们从一个单例模式开始:

    public final class MySingleton {  private MySingleton() { }  private static final MySingleton INSTANCE = new MySingleton();  public static MySingleton getInstance() { return INSTANCE; }  }

如果实现了序列化,那么会执行readObject方法或默认的序列化。他们都会返回一个新建的实例,也就违反了单例。

readResolve()方法正好满足需求:

    public final class MySingleton {  private MySingleton() { }  private static final MySingleton INSTANCE = new MySingleton();  public static MySingleton getInstance() { return INSTANCE; }  private Object readResolve() throws ObjectStreamException {  // instead of the object we're on,   // return the class variable INSTANCE  return INSTANCE;   }  }

书中提到了如果单例实例中存在非transient对象引用,就会有被攻击的危险,例子有点难懂,我在这里简单地解释一下;

因为单例包含一个非transient对象引用域,这个域内容在Singleton的readResolve方法运行之前被反序列化,于是,攻击者截胡,把这段readResolve方法运行之前的字节流截下来(不让他运行readResolve),来个偷梁换柱,把对象引用域指向自己写的“盗用者”。

于是,把readResolve方法运行之前的字节流 换成 偷梁换柱的字节流,再让他执行一次,系统看到单例对象有个非transient对象引用域,指向“盗用者”(因为偷梁换柱了),所以系统也会序列化“盗用者”。

盗用者是这样的:

class ElvisStealer implement Serializable{static Elvis impersonator;private Elvis payload;private Object readResolve(){impersonator = payload;return new String[]{"foolish"};}
}

序列化盗用者“ElvisStealer”的时候,执行它的readResolve()方法,impersonator = payload;偷偷 把第二次序列化的payload 记录下来(作为攻击者,其实没有必要,只是为了展示),然后返回一个错误的对象引用。

但是枚举类型就完全不同了:

// Enum singleton - the preferred approach
public enum Elvis {INSTANCE;private String[] favoriteSongs ={ "Hound Dog", "Heartbreak Hotel" };public void printFavorites() {System.out.println(Arrays.toString(favoriteSongs));
}
}

这样做完全没有后顾之忧,因为枚举enum生来就是支持序列化的,下面的官方对枚举的序列化的声明的翻译:

在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 我们看一下这个valueOf方法:

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,  String name) {  T result = enumType.enumConstantDirectory().get(name);  if (result != null)  return result;  if (name == null)  throw new NullPointerException("Name is null");  throw new IllegalArgumentException(  "No enum const " + enumType +"." + name);  }  

总结:尽可能用枚举类型来控制实例,否则,就要必须提供readResolve方法,并确保类的所有实例域都是基本类型或transient的。

Effective Java之对于实例控制,枚举类型优于readResolve(七十七)相关推荐

  1. Effective Java笔记第五章枚举和注解第三节用EnumSet代替位域

    Effective Java笔记第五章枚举和注解 第三节用EnumSet代替位域 在以前如果一个枚举类型的元素主要用在集合中,一般就会使用int枚举模式.比如说: public class Demo ...

  2. Effective Java读书笔记二:枚举和注解

    第30条:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 第31条:用实例域代替序数 枚举的ord ...

  3. java开发——什么时候使用枚举类型?为什么要用枚举类型?

    对于什么时候使用,下面举四个例子: 1.一周有多少天? 7天.像这样固定不变的一组数据,如果我们的程序有需要用到这"7天"的相关信息,例如:发工资分为工作日和周末,可以考虑使用枚举 ...

  4. java枚举变量带括号_java枚举类型 - 墨梅的个人空间 - OSCHINA - 中文开源技术交流社区...

    public class TestEnum { /*最普通的枚举*/ public enum ColorSelect { red, green, yellow, blue; } /* 枚举也可以象一般 ...

  5. 【Java面试题】定义枚举类型Weekday,使用枚举类型配合switch语句,尝试如下功能:

    定义枚举类型Weekday,使用枚举类型配合switch语句,尝试如下功能: wd = Mon是输出"Do Monday work",wd = Tue时,输出"Do Mo ...

  6. Java笔记-通过反射获取枚举类型中所有数据

    程序运行截图如下: 代码如下: Em.java package cn.it1995;public enum Em {ONE(1, "第一个枚举"),TWO(2, "第二个 ...

  7. Java设计模式——为什么要用枚举实现单例模式(避免反射、序列化问题)

    1.序言   相信如果能看到我这篇博客的小伙伴,肯定都看过Joshua Bloch大神说过的这句话:"单元素的枚举类型已经成为实现Singleton的最佳方法".其实,第一次读到这 ...

  8. Effective java 总结11 - 序列化

    Effective java 总结11 - 序列化 序列化:对象 -> 字节流 反序列化:字节流 -> 对象 第85条 其他方法优先于java序列化 序列化的根本问题在于:攻击面过于庞大, ...

  9. Effective Java读书笔记

    序列化 谨慎的实现Serializable接口 实现Serializable最大的代价,一旦这个类被发布就大大降低了改变这个类实现的灵活性,这个类中所有私有实例域都将变成导出API的一部分,不符合最低 ...

最新文章

  1. win10家庭版通过命令gpedit.msc打不开组策略的解决方法
  2. matlab如何绘制三维隐函数?
  3. php 上传文件漏洞,【文件上传】PHP文件上传漏洞
  4. 2020CCPC(长春) - Strange Memory(树上启发式合并+位运算)
  5. Sublime Text 2 中文包
  6. 服务端和客户端证书各种组合下对访问者(浏览器/中间人)的影响
  7. 同时对view延时执行两个动画时候的现象
  8. Word文档加密技巧
  9. unicode编码表_6-字符编码-文件处理
  10. debian软raid
  11. Java学习笔记(三):数组
  12. 磁盘空间未释放异常案例
  13. 【离散数学】代数系统 第六章 格与布尔代数(4) 布尔代数
  14. ToStringBuilder.reflectionToString用法
  15. 基于51单片机WiFi温湿度远程控制
  16. 怎么使用biopython_Biopython简单应用程序
  17. dedecms 自定义表单html,dedecms自定义表单和自定义表单如何用自己模板教程
  18. 【ATE-SENT协议】使用LabVIEW采集并解析SENT协议
  19. C#List子类转List父类或者Obj对象转List
  20. html用九张图片做出九宫图,.九图片详解和制作

热门文章

  1. [Windows驱动开发](二)基础知识——数据结构
  2. C++ 特殊类设计:只能在堆、栈上创建的类、无法继承的类、无法拷贝的类、只能创建一个对象的类
  3. 计算机网络 | 网络基础 :网络协议,协议分层,数据封装与分用,地址管理,字节序
  4. C语言程序设计 | 模拟实现内存操作函数:strncpy, strncat, strncmp, memcpy, memmove
  5. 超清晰的 DNS 原理入门指南
  6. 重学 Java 之 5种字符流读取方法
  7. linux下使用C++操作mysql
  8. 使用区分优先级的负载分流法确保Netflix的可靠性
  9. CDN关键技术研究与应用—内容路由技术
  10. 王荣刚:建立中国自主视频技术生态