Java的序列化API提供了一个框架,用来将对象编码成一个字节流(序列化,serializing),并从字节流中重新创建对象(反序列化, deserializing)。

第74条 谨慎地实现Serializable接口

  * 实现Serializable接口最大的代价是,一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性。

  * 实现Serializable接口的第二个代价是,它增加了出现Bug和安全漏洞的可能性。

  * 实现Serializable接口的第三个代价是,随着新版本的发布,相关的测试负担也增加了。

  * 内部类(inner class)不应该实现Serializable接口,内部类的默认序列化形式是定义不清楚的。而静态成员类(static member class)可以实现。

第75条 考虑使用自定义的序列化形式

  * 如果所有的实例域都是瞬时的(transient),从技术角度而言,不调用 defaultWriteObject 和 defaultReadObject 也是允许的,但不推荐这么做。

  * 被标记为transient的域,如果是对象引用域,则被初始化为null;如果是数值基本域,则为0;如果是boolean域,则为false。如果这些初始值不能被任何transient域接受,则需要提供一个readObject方法,先调用defaultReadObject,然后再将其恢复为可接受的值。

  * 声明一个显式的序列化版本id。

第76条 保护性的编写 readObject 方法

  * readObject方法实际上如同一个公有的构造器,如同其他构造器一样,也要注意同样的所有注意事项。

  * 编写健壮的readO方法:

    -- 对于对象引用域必须保持为私有的类,要保护性的拷贝这些域中的每一个对象。

    -- 对于任何约束条件,如果检查失败,则抛出一个InvalidObjectException异常。这些检查动作应该在跟在保护性拷贝之后。

    -- 如果整个对象图在被反序列化之后必须进行验证,就应该使用ObjectInputValidation接口。

    -- 无论是直接方式还是间接方式,都不要调用类中任何可被覆盖的方法。

第78条 对于实例控制,枚举类型优先于readResolve

  * 如果以下类实现Serializable 接口,那它将不再是单例:

 public class Singleon {
private static final Singleon INSTANCE = new Singleon(); private Singleon() { } // other codes
}
// 实现序列化接口后将不再是一个单例
public class Singleon implements Serializable {
private static final Singleon INSTANCE = new Singleon(); private Singleon() { } // other codes
}

  readResolve特性允许通过readObject创建的实例代替另一个实例。对于一个正在被反序列化的对象,如果它的类定义了一个readResolve方法,并且具备正确的声明,那么在反序列化之后,新建对象上的readResolve方法就会被调用。然后该方法返回的引用将被返回,取代新建的对象。

  如果上述的Singleon类实现了下面的方法,就足以保证它的单例属性:

     private Object readResolve() {
return INSTANCE;
}

  事实上,如果依赖readResolve进行实例控制,带有对象引用类型的所有实例域都必须声明为transient。否则,攻击者就有可能在readResolve方法被运行之前,保护指向反序列化的对象的引用。

  * 将一个可序列化的实例受控的类编写成枚举,则可以绝对保证除了声明的常量之外,不会有别的实例。

第78条 考虑使用序列化代理代替序列化实例

  * 序列化代理模式:

    (1)首先,增加一个静态内部类,其成员参数与需要序列化的外部类一致。

    (2)然后,在外部类中添加writeReplace方法。修改序列化时写入的内容,写入代理类的内容,而不是外部类的。

    (3)再在外部类添加readObject方法,让其抛出异常(由于写入的时候不是外部类的,那读的时候肯定也不是外部类的,如果是,则一定是伪造的)。

    (4)最后,在内部类中增加一个readResolve 方法,返回一个外部类实例。

  从整体上看,序列化代理模式就是增加一层转换。写入时先转换为代理类再写入,读出时再将其转换为被代理的类。

  完整代码如下:

 import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Date; /**
* @author Haoye
* @brief
* @detail
* @date 2018/10/6
* @see
*/
public class Period implements Serializable {
private static final long serialVersionUID = 1;
private final Date start;
private final Date end; public Period(Date start, Date end) {
this.start = start;
this.end = end;
} public Date getStart() {
return start;
} public Date getEnd() {
return end;
} /**
* writeReplace for the serialization proxy pattern
* @return instance of SerializationProxy
*/
private Object writeReplace() {
return new SerializationProxy(this);
} private void readObject(ObjectInputStream inputStream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
} /**
* proxy class
*/
private static class SerializationProxy implements Serializable{
private static final long serialVersionUID = 1;
private final Date start;
private final Date end; SerializationProxy(Period period) {
this.start = period.start;
this.end = period.end;
} private Object readResolve() {
return new Period(start, end);
}
} }

  

  * 序列化代理模式的好处:  

    (1)可以阻止伪字节流的攻击。

    (2)也可以阻止内部域的盗用攻击。

    (3)并且允许实际需要序列化的类的域声明为final。

    (4)序列化代理模式允许反序列化实例与原始序列化实例有着不同的类(如EnumSet的序列化代理)。

  EnumSet序列化代理类代码:

    /**
* This class is used to serialize all EnumSet instances, regardless of
* implementation type. It captures their "logical contents" and they
* are reconstructed using public static factories. This is necessary
* to ensure that the existence of a particular implementation type is
* an implementation detail.
*
* @serial include
*/
private static class SerializationProxy <E extends Enum<E>>
implements java.io.Serializable
{
/**
* The element type of this enum set.
*
* @serial
*/
private final Class<E> elementType; /**
* The elements contained in this enum set.
*
* @serial
*/
private final Enum<?>[] elements; SerializationProxy(EnumSet<E> set) {
elementType = set.elementType;
elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY);
} // instead of cast to E, we should perhaps use elementType.cast()
// to avoid injection of forged stream, but it will slow the implementation
@SuppressWarnings("unchecked")
private Object readResolve() {
EnumSet<E> result = EnumSet.noneOf(elementType);
for (Enum<?> e : elements)
result.add((E)e);
return result;
} private static final long serialVersionUID = 362491234563181265L;
}

本文地址:https://www.cnblogs.com/laishenghao/p/effective_java_note_serialization.html

《Effective Java》学习笔记 —— 序列化的更多相关文章

  1. Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法

    避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...

  2. Effective Java学习笔记

    创建和销毁对象 第一条:考虑用静态工厂方法替代构造器 For example: public static Boolean valueOf(boolean b){ return b ? Boolean ...

  3. Effective Java 学习笔记之创建和销毁对象

    一.考虑用静态工厂方法代替构造器 1.此处的静态工厂方法是指返回指为类的对象的静态方法,而不是设计模式中的静态工厂方法. 2.静态工厂方法的优势有: a.使用不同的方法名称可显著地表明两个静态工厂方法 ...

  4. Java学习笔记——序列化和反序列化

    寒雨连江夜入吴,平明送客楚山孤. 洛阳亲友如相问,一片冰心在玉壶. --芙蓉楼送辛渐 持久化数据的第一种方式.在序列化之前也可以把数据打散逐行存储在文件中,然后在逐行读取. 比如定Student类 用 ...

  5. Effective Java 学习笔记之所有对象都通用的方法

    一.覆盖equals时请遵守通用约定 1.满足下列任何一个条件时,不需要覆盖equals方法 a.类的每个实例本质上都是唯一的.此时就是Object中equals方法所表达的含义. b.不关心类是否提 ...

  6. Effective Java 学习笔记----第7章 通用程序设计

    第7章 通用程序设计 第29条 将局部变量的作用域最小化     使一个局部变量的作用域最小化,最有力的技术室在第一次使用它的地方声明.   第30条 了解和使用库      效率提高.如果你不知道库 ...

  7. effective java学习笔记之不可实例化的类

    在没有显式声明一个类的构造方法时,编译器会生成默认的无参构造方法,在设计工具类时,我们通常将方法设置成静态方法,以类名.方法名的形式调用,此时这个类就没有必要创建实例,我们知道抽象类不可以被实例化,但 ...

  8. Effective Java学习笔记--创建和销毁对象

    创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...

  9. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  10. java学习笔记10--枚举

    java学习笔记10--枚举 在JDK1.5之前,java可以有两种方式定义新类型:类和接口.对于大部分面向对 象编程来说,这两种方法看起来似乎足够了,但是在一些特殊情况下,这些方法就不适合.例如,想 ...

随机推荐

  1. SVN 远程访问

    第一种方法 https://www.cnblogs.com/Leo_wl/p/3475167.html#_label0 默认协议为:https 端口号:443 服务器地址:https://主机名/sv ...

  2. .net core 入坑经验 - 2、MVC Core之获取网站运行路径

    这次是建立了asp.net mvc core项目,在controller中想获取网站在硬盘中的路径,找了一圈Server.MapPath() 已不存在,HttpContent也一样,经过查阅资料发现是 ...

  3. November 15th, 2017 Week 46th Wednesday

    Of all the tribulations in this world, boredom is the one most hard to bear. 所有的苦难中,无聊是最难以忍受的. When ...

  4. BZOJ 1113 海报 单调栈

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1113 题目大意: N个矩形,排成一排. 现在希望用尽量少的矩形海报Cover住它们. ...

  5. QT5 视图坐标

    又出错了. . main.obj:-1: error: LNK2001: 无法解析的外部符号 "public: virtual struct QMetaObject const * __th ...

  6. CSS 文本

    CSS 文本属性可定义文本的外观. 通过文本属性,您可以改变文本的颜色.字符间距,对齐文本,装饰文本,对文本进行缩进,等等. 缩进文本 把 Web 页面上的段落的第一行缩进,这是一种最常用的文本格式化 ...

  7. 构造方法、 This关键字 、static、封装

    1.1 构造方法 构造方法是一种特殊的方法,专门用于构造/实例化对象,形式: [修饰符] 类名(){ } 构造方法根据是否有参数分为无参构造和有参构. 1.1.1 无参构造 无参构造方法就是构造方法没 ...

  8. shiro实战系列(十五)之Spring集成Shiro

    Shiro 的 JavaBean 兼容性使得它非常适合通过 Spring XML 或其他基于 Spring 的配置机制.Shiro 应用程序需要一个具 有单例 SecurityManager 实例的应 ...

  9. /bin/ls: Permission denied

    [root@test_node1 ~]# crontab -lno crontab for root[root@test_node1 ~]# cd /home/[root@test_node1 hom ...

  10. 搞死人的contextRoot;weblogic9.2

    默认情况下: 两个app-deployment同时部署到了一台weblogic服务器的同一个domain下面的时候 /mysite/www/www/WEB-INF/weblogic.xml /mysi ...