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. Oracle EBS OM 已存在的OM订单增加物料

    DECLARE l_header_rec OE_ORDER_PUB.Header_Rec_Type; l_line_tbl OE_ORDER_PUB.Line_Tbl_Type; l_action_r ...

  2. apache 允许 访问软链接 ( Apache won't follow symlinks (403 Forbidden) )

    当我们在 apache 中 进行访问 www 文件夹之外的目录的时候,我们可以使用软链接的方式来进行协助访问. 我在 html 文件夹 下面创建 了 如下软链接 link: [root@dhcp-- ...

  3. ecplice 中添加JavaFX插件学习

    fxml文件使用SceneBuilder打开报错 解决方法:Window-->Preferences-->JavaFX-->browse 路径是可执行的JavaFX Scene Bu ...

  4. Qt: QAction在QToolBar中快捷键行为注意事项

    在QMenuBar中添加快捷键很简单,只要在text的特定字母前加&,如&k按下ALT+k即会触发(QPushButton也是一样).但在QToolBar则不然,需要调action-& ...

  5. Linux之因BASH造成的键盘错误和环境问题

    对于Linux我们习惯使用/bin/bash.并且大多数人操作在Centos系统上,但是仍有不少人在ubuntu上使用,两个操作系统大同小异.都是使用了Linux内核.接下来就来讲讲我使用过程中两个系 ...

  6. 开源作业调度框架 - Quartz.NET - 实战使用2

    纠正第一篇文章的一个错误代码. 下面是错误代码,这样并不能得知系统中是否已经存在该JobId //实例化一个作业Key对象,用于获取作业对象或判断是否存在作业时使用. JobKey jobKey = ...

  7. October 24th, 2017 Week 43rd Tuesday

    We can't give up trying. The fight was worth it. 我们不能放弃尝试,奋斗是值得的. When doing researches in some cutt ...

  8. vue 文本比较插件

    npm install codemirror diff-match-patch diff-match-patch template: <div id="view">&l ...

  9. UMI开源项目

    本文主要围绕UMI是什么及其特征.安装应用.模板例子等四个方面内容来讲解UMI,希望能够对初学者有所启发. 一. UMI是什么 UMI是可插拔的企业级反应应用程序框架. 二. 特征 特征

  10. 在openresty或nginx编译nginx-upsync-module&nginx_upstream_check_module

    针对我在编译在两个模块的过程中遇到的一系列问题,特此记录编译流程的一些细节 1.下载 install git git clone https://github.com/weibocom/nginx-u ...