mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-31 19:13:42 +00:00
Update 设计模式 - 单例.md
补充枚举单例防止反序列化破坏的原因和几种写法的选择,增加参考资料。
This commit is contained in:
+5
-1
@@ -184,7 +184,9 @@ java.lang.IllegalArgumentException: Cannot reflectively create enum objects
|
||||
|
||||
该实现可以防止反射攻击。在其它实现中,调用私有构造函数的 setAccessible(true) 可以绕过访问检查,再调用 newInstance() 创建新的实例;要防止这种攻击,可以在构造函数中检查实例是否已经存在并抛出异常,但这只能挡住实例创建之后的反射调用:懒汉式在第一次调用 getUniqueInstance() 之前被反射创建时,检查不出来。对于枚举类型,Constructor.newInstance() 直接抛出 IllegalArgumentException,因此不会出现上述的反射攻击。
|
||||
|
||||
该实现在多次序列化和反序列化之后,不会得到多个实例。而其它实现要实现 Serializable,反序列化时每次都会创建一个新对象,需要提供 readResolve() 方法返回已有的实例来替换它;transient 只决定字段是否参与序列化,不能保证实例唯一。
|
||||
该实现在多次序列化和反序列化之后,不会得到多个实例。这是因为枚举常量序列化时只写入它的名字,反序列化时用 Enum.valueOf() 按名字找回已有的常量,这个过程不能自定义。而其它实现要实现 Serializable,反序列化时每次都会创建一个新对象,需要提供 readResolve() 方法返回已有的实例来替换它;transient 只决定字段是否参与序列化,不能保证实例唯一。
|
||||
|
||||
这几种写法中,《Effective Java》推荐单元素的枚举,它同时挡住了反射和反序列化;枚举不能继承其它类,单例需要继承某个父类时,用静态内部类实现。
|
||||
|
||||
### 示例
|
||||
- 日志类;
|
||||
@@ -206,3 +208,5 @@ Spring 默认的单例 Bean 与这里的单例不同:它指的是一个容器
|
||||
|
||||
- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007.
|
||||
- Gamma E. 设计模式:可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.
|
||||
- Bloch J. Effective Java(第 3 版) [M]. Addison-Wesley, 2018.
|
||||
- [Java Object Serialization Specification: Serialization of Enum Constants](https://docs.oracle.com/en/java/javase/21/docs/specs/serialization/serial-arch.html)
|
||||
|
||||
Reference in New Issue
Block a user