欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: 拿笔小星_

《Effective Java》已经汇报我们,在单例类中提供一个readResolve要领就可以完成单例特性。这里各人可以本身去测试。

接下来,我们去看看Java提供的反序列化是如何建设工具的!

ObjectInputStream

工具的序列化进程通过ObjectOutputStream和ObjectInputputStream来实现的,那么带着方才的问题,阐明一下ObjectInputputStream的readObject 要领执行环境到底是奈何的。

为了节减篇幅,这里给出ObjectInputStream的readObject的挪用栈:

) 详细实现: public stat  <a href=苏州软件公司 ic T extends EnumT T valueOf(ClassT enumType" class="aligncenter size-full wp-image-29344" title="未定名文件-1" src="/uploads/allimg/c180802/1533153Z060930-14242.png" />

各人顺着此图的干系,昆山软件开发,去看readObject要领的实现。
首先进入readObject0要领里,要害代码如下:

switch (tc) {
    //省略部门代码

    case TC_STRING:
    case TC_LONGSTRING:
        return checkResolve(readString(unshared));

    case TC_ARRAY:
        return checkResolve(readArray(unshared));

    case TC_ENUM:
        return checkResolve(readEnum(unshared));

    case TC_OBJECT:
        return checkResolve(readOrdinaryObject(unshared));

    case TC_EXCEPTION:
        IOException ex = readFatalException();
        throw new WriteAbortedException("writing aborted", ex);

    case TC_BLOCKDATA:
    case TC_BLOCKDATALONG:
        if (oldMode) {
            bin.setBlockDataMode(true);
            bin.peek();             // force header read
            throw new OptionalDataException(
                bin.currentBlockRemaining());
        } else {
            throw new StreamCorruptedException(
                "unexpected block data");
        }

    //省略部门代码

这里就是判定方针工具的范例,差异范例执行差异的行动。我们的是个普通的Object工具,自然就是进入case TC_OBJECT的代码块中。然后进入readOrdinaryObject要领中。
readOrdinaryObject要领的代码片断:

private Object readOrdinaryObject(boolean unshared)
        throws IOException {
    //此处省略部门代码

    Object obj;
    try {
        obj = desc.isInstantiable() ? desc.newInstance() : null;
    } catch (Exception ex) {
        throw (IOException) new InvalidClassException(
            desc.forClass().getName(),
            "unable to create instance").initCause(ex);
    }

    //此处省略部门代码

    if (obj != null &&
        handles.lookupException(passHandle) == null &&
        desc.hasReadResolveMethod())
    {
        Object rep = desc.invokeReadResolve(obj);
        if (unshared && rep.getClass().isArray()) {
            rep = cloneArray(rep);
        }
        if (rep != obj) {
            handles.setObject(passHandle, obj = rep);
        }
    }

    return obj;
}

重点看代码块:

 Object obj;
 try {
      obj = desc.isInstantiable() ? desc.newInstance() : null;
  } catch (Exception ex) {
      throw (IOException) new InvalidClassException(
          desc.forClass().getName(),
          "unable to create instance").initCause(ex);
  }

这里建设的这个obj工具,就是本要领要返回的工具,也可以临时领略为是ObjectInputStream的readObject返回的工具。

isInstantiable:假如一个serializable/externalizable的类可以在运行时被实例化,那么该要领就返回true。针对serializable和externalizable我会在其他文章中先容。 
desc.newInstance:该要领通过反射的方法挪用无参结构要领新建一个工具。

所以。到今朝为止,也就可以表明,为什么序列化可以粉碎单例了?即序列化会通过反射挪用无参数的结构要领建设一个新的工具。

接下来再看,为什么在单例类中界说readResolve就可以办理该问题呢?照旧在readOrdinaryObjec要领里继承往下看。

if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
{
    Object rep = desc.invokeReadResolve(obj);
     if (unshared && rep.getClass().isArray()) {
         rep = cloneArray(rep);
     }
     if (rep != obj) {
         handles.setObject(passHandle, obj = rep);
     }
}

这段代码也很清楚地给出谜底了!
假如方针类有readResolve要领,那就通过反射的方法挪用要被反序列化的类的readResolve要领,返回一个工具,然后把这个新的工具复制给之前建设的obj(即最终返回的工具)。那readResolve 要领里是什么?就是直接返回我们的单例工具。

public class Elvis implements Serializable {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() { 
        System.err.println("Elvis Constructor is invoked!");
    }

    private Object readResolve() {
       return INSTANCE;
    }
}