(从09年回到重庆过后,就一直在工作,时间长了惰性就慢慢起来了,公司的项目从09年忙到了现在,一直没有时间来梳理自己的东西,CSDN的Blog似乎都荒废了,不知道现在还能否坚持把Blog完成,希望有一个新的开始吧!如果读者有问题还是可直接发邮件到[email protected],我也仅仅只是想把看的、写的、学的东西总结起来,让自己有个比较完整的学习记录。本文主要针对Java的序列化相关知识,先不涉及XML序列化和Json序列化的内容,这部分内容以后再议。着色的目的是强调重点和关键的概念以及防止读者通篇阅读的视觉疲劳,也是个人的写作风格,不习惯的读者请见谅!)
本章目录:
1.Java中的序列化
2.序列化原理和算法——基础数据
3.深入序列化规范
4.源码分析
----ObjectStreamField
----ObjectStreamClass
----ObjectOutputStream
----ObjectInputStream
5.序列化原理和算法——面向对象
4.源码分析
【本章节的内容主要做的是源码分析,分析过程的概念性内容都是基于个人的理解,仅仅提供给读者参考,我尽可能在分析过程中保证这些概念性内容的准确性】
前边的章节已经辅助读者解读了JVM的规范以及提供了比较详细的示例,这个章节主要完成下边三个目标:
public class ObjectStreamField implements Comparable
public int compareTo(Object obj) {
ObjectStreamField other = (ObjectStreamField) obj;
boolean isPrim = isPrimitive();
if (isPrim != other.isPrimitive()) {
return isPrim ? -1 : 1;
}
return name.compareTo(other.name);
}
public boolean isPrimitive() {
char tcode = signature.charAt(0);
return ((tcode != 'L') && (tcode != '['));
}
/** field name */
private final String name;
/** canonical JVM signature of field type */
private final String signature;
/** field type (Object.class if unknown non-primitive type) */
private final Class> type;
/** whether or not to (de)serialize field values as unshared */
private final boolean unshared;
/** corresponding reflective field object, if any */
private final Field field;
/** offset of field value in enclosing field group */
private int offset = 0;
Class cs = String.class;
类型代码——类型代码的数据也是用于JVM判断成员属性数据类型的一种方式,但类型代码的Java数据类型是char,比如‘L’,它一般通过一个字符来判断当前的Java数据类型,序列化时它会把这个字符转换成二进制数据;
public String getName() {
return name;
}
String getSignature() {
return signature;
}
public Class> getType() {
return type;
}
public boolean isUnshared() {
return unshared;
}
Field getField() {
return field;
}
public int getOffset() {
return offset;
}
protected void setOffset(int offset) {
this.offset = offset;
}
public ObjectStreamField(String name, Class> type) {
this(name, type, false);
}
public ObjectStreamField(String name, Class> type, boolean unshared) {
if (name == null) {
throw new NullPointerException();
}
this.name = name;
this.type = type;
this.unshared = unshared;
signature = getClassSignature(type).intern();
field = null;
}
ObjectStreamField(String name, String signature, boolean unshared) {
if (name == null) {
throw new NullPointerException();
}
this.name = name;
this.signature = signature.intern();
this.unshared = unshared;
field = null;
switch (signature.charAt(0)) {
case 'Z': type = Boolean.TYPE; break;
case 'B': type = Byte.TYPE; break;
case 'C': type = Character.TYPE; break;
case 'S': type = Short.TYPE; break;
case 'I': type = Integer.TYPE; break;
case 'J': type = Long.TYPE; break;
case 'F': type = Float.TYPE; break;
case 'D': type = Double.TYPE; break;
case 'L':
case '[': type = Object.class; break;
default: throw new IllegalArgumentException("illegal signature");
}
}
ObjectStreamField(Field field, boolean unshared, boolean showType) {
this.field = field;
this.unshared = unshared;
name = field.getName();
Class> ftype = field.getType();
type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
signature = getClassSignature(ftype).intern();
}
private static String getClassSignature(Class> cl) {
StringBuilder sbuf = new StringBuilder();
while (cl.isArray()) {
sbuf.append('[');
cl = cl.getComponentType();
}
if (cl.isPrimitive()) {
if (cl == Integer.TYPE) {
sbuf.append('I');
} else if (cl == Byte.TYPE) {
sbuf.append('B');
} else if (cl == Long.TYPE) {
sbuf.append('J');
} else if (cl == Float.TYPE) {
sbuf.append('F');
} else if (cl == Double.TYPE) {
sbuf.append('D');
} else if (cl == Short.TYPE) {
sbuf.append('S');
} else if (cl == Character.TYPE) {
sbuf.append('C');
} else if (cl == Boolean.TYPE) {
sbuf.append('Z');
} else if (cl == Void.TYPE) {
sbuf.append('V');
} else {
throw new InternalError();
}
} else {
sbuf.append('L' + cl.getName().replace('.', '/') + ';');
}
return sbuf.toString();
}
package org.susan.java.serial;
import java.util.ArrayList;
import java.util.List;
public class FieldSignature {
public static String getClassSignature(Class> cl) {
StringBuilder sbuf = new StringBuilder();
while (cl.isArray()) {
sbuf.append('[');
cl = cl.getComponentType();
}
if (cl.isPrimitive()) {
if (cl == Integer.TYPE) {
sbuf.append('I');
} else if (cl == Byte.TYPE) {
sbuf.append('B');
} else if (cl == Long.TYPE) {
sbuf.append('J');
} else if (cl == Float.TYPE) {
sbuf.append('F');
} else if (cl == Double.TYPE) {
sbuf.append('D');
} else if (cl == Short.TYPE) {
sbuf.append('S');
} else if (cl == Character.TYPE) {
sbuf.append('C');
} else if (cl == Boolean.TYPE) {
sbuf.append('Z');
} else if (cl == Void.TYPE) {
sbuf.append('V');
} else {
throw new InternalError();
}
} else {
sbuf.append('L' + cl.getName().replace('.', '/') + ';');
}
return sbuf.toString();
}
public static void main(String args[]){
// void类型
System.out.println(FieldSignature.getClassSignature(void.class));
// 基础类型
System.out.println(FieldSignature.getClassSignature(int.class));
// 基础封装类型
System.out.println(FieldSignature.getClassSignature(Integer.class));
// 自定义对象类型
System.out.println(FieldSignature.getClassSignature(FieldSignature.class));
// String类型
System.out.println(FieldSignature.getClassSignature(String.class));
// 数组类型
int[] arr = new int[2];
System.out.println(FieldSignature.getClassSignature(arr.getClass()));
// 对象数组
FieldSignature[] signs = new FieldSignature[4];
System.out.println(FieldSignature.getClassSignature(signs.getClass()));
// 对象集合
List list = new ArrayList();
System.out.println(FieldSignature.getClassSignature(list.getClass()));
}
}
public char getTypeCode() {
return signature.charAt(0);
}
public String getTypeString() {
return isPrimitive() ? null : signature;
}
public class ObjectStreamClass implements Serializable
private static class Caches
private static class ExceptionInfo
private static class EntryFuture
static class ClassDataSlot
private static class MemberSignature
private static class FieldReflector
private static class FieldReflectorKey extends WeakReference>
static class WeakClassKey extends WeakReference>
private static class Caches {
/** cache mapping local classes -> descriptors */
static final ConcurrentMap> localDescs =
new ConcurrentHashMap<>();
/** cache mapping field group/local desc pairs -> field reflectors */
static final ConcurrentMap> reflectors =
new ConcurrentHashMap<>();
/** queue for WeakReferences to local classes */
private static final ReferenceQueue> localDescsQueue =
new ReferenceQueue<>();
/** queue for WeakReferences to field reflectors keys */
private static final ReferenceQueue> reflectorsQueue =
new ReferenceQueue<>();
}
private static class ExceptionInfo {
private final String className;
private final String message;
ExceptionInfo(String cn, String msg) {
className = cn;
message = msg;
}
/**
* Returns (does not throw) an InvalidClassException instance created
* from the information in this object, suitable for being thrown by
* the caller.
*/
InvalidClassException newInvalidClassException() {
return new InvalidClassException(className, message);
}
}
private static class EntryFuture {
private static final Object unset = new Object();
private final Thread owner = Thread.currentThread();
private Object entry = unset;
/**
* Attempts to set the value contained by this EntryFuture. If the
* EntryFuture's value has not been set already, then the value is
* saved, any callers blocked in the get() method are notified, and
* true is returned. If the value has already been set, then no saving
* or notification occurs, and false is returned.
*/
synchronized boolean set(Object entry) {
if (this.entry != unset) {
return false;
}
this.entry = entry;
notifyAll();
return true;
}
/**
* Returns the value contained by this EntryFuture, blocking if
* necessary until a value is set.
*/
synchronized Object get() {
boolean interrupted = false;
while (entry == unset) {
try {
wait();
} catch (InterruptedException ex) {
interrupted = true;
}
}
if (interrupted) {
AccessController.doPrivileged(
new PrivilegedAction() {
public Void run() {
Thread.currentThread().interrupt();
return null;
}
}
);
}
return entry;
}
/**
* Returns the thread that created this EntryFuture.
*/
Thread getOwner() {
return owner;
}
}
static class ClassDataSlot {
/** class descriptor "occupying" this slot */
final ObjectStreamClass desc;
/** true if serialized form includes data for this slot's descriptor */
final boolean hasData;
ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
this.desc = desc;
this.hasData = hasData;
}
}
private static class MemberSignature {
public final Member member;
public final String name;
public final String signature;
public MemberSignature(Field field) {
member = field;
name = field.getName();
signature = getClassSignature(field.getType());
}
public MemberSignature(Constructor cons) {
member = cons;
name = cons.getName();
signature = getMethodSignature(
cons.getParameterTypes(), Void.TYPE);
}
public MemberSignature(Method meth) {
member = meth;
name = meth.getName();
signature = getMethodSignature(
meth.getParameterTypes(), meth.getReturnType());
}
}
public MemberSignature(Field field)
public MemberSignature(Constructor cons)
public MemberSignature(Method meth)
private static String getMethodSignature(Class>[] paramTypes,
Class> retType)
{
StringBuilder sbuf = new StringBuilder();
sbuf.append('(');
for (int i = 0; i < paramTypes.length; i++) {
sbuf.append(getClassSignature(paramTypes[i]));
}
sbuf.append(')');
sbuf.append(getClassSignature(retType));
return sbuf.toString();
}
package org.susan.java.serial;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class MethodSignature {
public static String getMethodSignature(Class>[] paramTypes,
Class> retType) {
StringBuilder sbuf = new StringBuilder();
sbuf.append('(');
for (int i = 0; i < paramTypes.length; i++) {
sbuf.append(FieldSignature.getClassSignature(paramTypes[i]));
}
sbuf.append(')');
sbuf.append(FieldSignature.getClassSignature(retType));
return sbuf.toString();
}
public static void main(String args[]){
// 构造函数签名
Constructor>[] cons = MethodTest.class.getDeclaredConstructors();
for( Constructor> con: cons){
System.out.println("==>" + con.getName());
System.out.println(getMethodSignature(con.getParameterTypes(),Void.TYPE));
}
// 方法签名
Method[] methods = MethodTest.class.getDeclaredMethods();
for( Method method: methods){
System.out.println("==>" + method.getName());
System.out.println(getMethodSignature(method.getParameterTypes(),method.getReturnType()));
}
}
}
class MethodTest{
private int age;
private String name;
public MethodTest(int age, String name){
this.age = age;
this.name = name;
}
public MethodTest(int age){
this(age,"Unknown");
}
public String buildTemplate(String prefix){
return prefix + " " + this.name + ", you are " + this.age + " now.";
}
public void buildHello(){
String template = buildTemplate("Hello");
System.out.println(template);
}
}
private static class FieldReflectorKey extends WeakReference> {
private final String sigs;
private final int hash;
private final boolean nullClass;
FieldReflectorKey(Class> cl, ObjectStreamField[] fields,
ReferenceQueue> queue)
{
super(cl, queue);
nullClass = (cl == null);
StringBuilder sbuf = new StringBuilder();
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
sbuf.append(f.getName()).append(f.getSignature());
}
sigs = sbuf.toString();
hash = System.identityHashCode(cl) + sigs.hashCode();
}
public int hashCode() {
return hash;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof FieldReflectorKey) {
FieldReflectorKey other = (FieldReflectorKey) obj;
Class> referent;
return (nullClass ? other.nullClass
: ((referent = get()) != null) &&
(referent == other.get())) &&
sigs.equals(other.sigs);
} else {
return false;
}
}
}
static class WeakClassKey extends WeakReference> {
/**
* saved value of the referent's identity hash code, to maintain
* a consistent hash code after the referent has been cleared
*/
private final int hash;
/**
* Create a new WeakClassKey to the given object, registered
* with a queue.
*/
WeakClassKey(Class> cl, ReferenceQueue> refQueue) {
super(cl, refQueue);
hash = System.identityHashCode(cl);
}
/**
* Returns the identity hash code of the original referent.
*/
public int hashCode() {
return hash;
}
/**
* Returns true if the given object is this identical
* WeakClassKey instance, or, if this object's referent has not
* been cleared, if the given object is another WeakClassKey
* instance with the identical non-null referent as this one.
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof WeakClassKey) {
Object referent = get();
return (referent != null) &&
(referent == ((WeakClassKey) obj).get());
} else {
return false;
}
}
}
/** handle for performing unsafe operations */
private static final Unsafe unsafe = Unsafe.getUnsafe();
/** fields to operate on */
private final ObjectStreamField[] fields;
/** number of primitive fields */
private final int numPrimFields;
/** unsafe field keys for reading fields - may contain dupes */
private final long[] readKeys;
/** unsafe fields keys for writing fields - no dupes */
private final long[] writeKeys;
/** field data offsets */
private final int[] offsets;
/** field type codes */
private final char[] typeCodes;
/** field types */
private final Class>[] types;
ObjectStreamField[] getFields() {
return fields;
}
void getPrimFieldValues(Object obj, byte[] buf) {...}
void setPrimFieldValues(Object obj, byte[] buf) {...}
void getObjFieldValues(Object obj, Object[] vals) {...}
void setObjFieldValues(Object obj, Object[] vals) {...}
FieldReflector(ObjectStreamField[] fields) {
this.fields = fields;
int nfields = fields.length;
readKeys = new long[nfields];
writeKeys = new long[nfields];
offsets = new int[nfields];
typeCodes = new char[nfields];
ArrayList> typeList = new ArrayList<>();
Set usedKeys = new HashSet<>();
for (int i = 0; i < nfields; i++) {
ObjectStreamField f = fields[i];
Field rf = f.getField();
long key = (rf != null) ?
unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
readKeys[i] = key;
writeKeys[i] = usedKeys.add(key) ?
key : Unsafe.INVALID_FIELD_OFFSET;
offsets[i] = f.getOffset();
typeCodes[i] = f.getTypeCode();
if (!f.isPrimitive()) {
typeList.add((rf != null) ? rf.getType() : null);
}
}
types = typeList.toArray(new Class>[typeList.size()]);
numPrimFields = nfields - types.length;
}
private static native void initNative();
static {
initNative();
}
private native static boolean hasStaticInitializer(Class> cl);
private static final long serialVersionUID = -6120832682080437368L;
/** serialVersionUID of represented class (null if not computed yet) */
private volatile Long suid;
public long getSerialVersionUID() {
// REMIND: synchronize instead of relying on volatile?
if (suid == null) {
suid = AccessController.doPrivileged(
new PrivilegedAction() {
public Long run() {
return computeDefaultSUID(cl);
}
}
);
}
return suid.longValue();
}
private static Long getDeclaredSUID(Class> cl) {
try {
Field f = cl.getDeclaredField("serialVersionUID");
int mask = Modifier.STATIC | Modifier.FINAL;
if ((f.getModifiers() & mask) == mask) {
f.setAccessible(true);
return Long.valueOf(f.getLong(null));
}
} catch (Exception ex) {
}
return null;
}
private static long computeDefaultSUID(Class> cl) {
if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
{
return 0L;
}
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
dout.writeUTF(cl.getName());
int classMods = cl.getModifiers() &
(Modifier.PUBLIC | Modifier.FINAL |
Modifier.INTERFACE | Modifier.ABSTRACT);
/*
* compensate for javac bug in which ABSTRACT bit was set for an
* interface only if the interface declared methods
*/
Method[] methods = cl.getDeclaredMethods();
if ((classMods & Modifier.INTERFACE) != 0) {
classMods = (methods.length > 0) ?
(classMods | Modifier.ABSTRACT) :
(classMods & ~Modifier.ABSTRACT);
}
dout.writeInt(classMods);
if (!cl.isArray()) {
/*
* compensate for change in 1.2FCS in which
* Class.getInterfaces() was modified to return Cloneable and
* Serializable for array classes.
*/
Class>[] interfaces = cl.getInterfaces();
String[] ifaceNames = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
ifaceNames[i] = interfaces[i].getName();
}
Arrays.sort(ifaceNames);
for (int i = 0; i < ifaceNames.length; i++) {
dout.writeUTF(ifaceNames[i]);
}
}
Field[] fields = cl.getDeclaredFields();
MemberSignature[] fieldSigs = new MemberSignature[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldSigs[i] = new MemberSignature(fields[i]);
}
Arrays.sort(fieldSigs, new Comparator() {
public int compare(MemberSignature ms1, MemberSignature ms2) {
return ms1.name.compareTo(ms2.name);
}
});
for (int i = 0; i < fieldSigs.length; i++) {
MemberSignature sig = fieldSigs[i];
int mods = sig.member.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
Modifier.TRANSIENT);
if (((mods & Modifier.PRIVATE) == 0) ||
((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
{
dout.writeUTF(sig.name);
dout.writeInt(mods);
dout.writeUTF(sig.signature);
}
}
if (hasStaticInitializer(cl)) {
dout.writeUTF("");
dout.writeInt(Modifier.STATIC);
dout.writeUTF("()V");
}
Constructor[] cons = cl.getDeclaredConstructors();
MemberSignature[] consSigs = new MemberSignature[cons.length];
for (int i = 0; i < cons.length; i++) {
consSigs[i] = new MemberSignature(cons[i]);
}
Arrays.sort(consSigs, new Comparator() {
public int compare(MemberSignature ms1, MemberSignature ms2) {
return ms1.signature.compareTo(ms2.signature);
}
});
for (int i = 0; i < consSigs.length; i++) {
MemberSignature sig = consSigs[i];
int mods = sig.member.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL |
Modifier.SYNCHRONIZED | Modifier.NATIVE |
Modifier.ABSTRACT | Modifier.STRICT);
if ((mods & Modifier.PRIVATE) == 0) {
dout.writeUTF("");
dout.writeInt(mods);
dout.writeUTF(sig.signature.replace('/', '.'));
}
}
MemberSignature[] methSigs = new MemberSignature[methods.length];
for (int i = 0; i < methods.length; i++) {
methSigs[i] = new MemberSignature(methods[i]);
}
Arrays.sort(methSigs, new Comparator() {
public int compare(MemberSignature ms1, MemberSignature ms2) {
int comp = ms1.name.compareTo(ms2.name);
if (comp == 0) {
comp = ms1.signature.compareTo(ms2.signature);
}
return comp;
}
});
for (int i = 0; i < methSigs.length; i++) {
MemberSignature sig = methSigs[i];
int mods = sig.member.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL |
Modifier.SYNCHRONIZED | Modifier.NATIVE |
Modifier.ABSTRACT | Modifier.STRICT);
if ((mods & Modifier.PRIVATE) == 0) {
dout.writeUTF(sig.name);
dout.writeInt(mods);
dout.writeUTF(sig.signature.replace('/', '.'));
}
}
dout.flush();
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] hashBytes = md.digest(bout.toByteArray());
long hash = 0;
for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
hash = (hash << 8) | (hashBytes[i] & 0xFF);
}
return hash;
} catch (IOException ex) {
throw new InternalError();
} catch (NoSuchAlgorithmException ex) {
throw new SecurityException(ex.getMessage());
}
}
if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
{
return 0L;
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
dout.writeUTF(cl.getName());
int classMods = cl.getModifiers() &
(Modifier.PUBLIC | Modifier.FINAL |
Modifier.INTERFACE | Modifier.ABSTRACT);
/*
* compensate for javac bug in which ABSTRACT bit was set for an
* interface only if the interface declared methods
*/
Method[] methods = cl.getDeclaredMethods();
if ((classMods & Modifier.INTERFACE) != 0) {
classMods = (methods.length > 0) ?
(classMods | Modifier.ABSTRACT) :
(classMods & ~Modifier.ABSTRACT);
}
dout.writeInt(classMods);
if (!cl.isArray()) {
/*
* compensate for change in 1.2FCS in which
* Class.getInterfaces() was modified to return Cloneable and
* Serializable for array classes.
*/
Class>[] interfaces = cl.getInterfaces();
String[] ifaceNames = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
ifaceNames[i] = interfaces[i].getName();
}
Arrays.sort(ifaceNames);
for (int i = 0; i < ifaceNames.length; i++) {
dout.writeUTF(ifaceNames[i]);
}
}
Field[] fields = cl.getDeclaredFields();
MemberSignature[] fieldSigs = new MemberSignature[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldSigs[i] = new MemberSignature(fields[i]);
}
Arrays.sort(fieldSigs, new Comparator() {
public int compare(MemberSignature ms1, MemberSignature ms2) {
return ms1.name.compareTo(ms2.name);
}
});
for (int i = 0; i < fieldSigs.length; i++) {
MemberSignature sig = fieldSigs[i];
int mods = sig.member.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
Modifier.TRANSIENT);
if (((mods & Modifier.PRIVATE) == 0) ||
((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
{
dout.writeUTF(sig.name);
dout.writeInt(mods);
dout.writeUTF(sig.signature);
}
}
if (hasStaticInitializer(cl)) {
dout.writeUTF("");
dout.writeInt(Modifier.STATIC);
dout.writeUTF("()V");
}
Constructor[] cons = cl.getDeclaredConstructors();
MemberSignature[] consSigs = new MemberSignature[cons.length];
for (int i = 0; i < cons.length; i++) {
consSigs[i] = new MemberSignature(cons[i]);
}
Arrays.sort(consSigs, new Comparator() {
public int compare(MemberSignature ms1, MemberSignature ms2) {
return ms1.signature.compareTo(ms2.signature);
}
});
for (int i = 0; i < consSigs.length; i++) {
MemberSignature sig = consSigs[i];
int mods = sig.member.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL |
Modifier.SYNCHRONIZED | Modifier.NATIVE |
Modifier.ABSTRACT | Modifier.STRICT);
if ((mods & Modifier.PRIVATE) == 0) {
dout.writeUTF("");
dout.writeInt(mods);
dout.writeUTF(sig.signature.replace('/', '.'));
}
}
MemberSignature[] methSigs = new MemberSignature[methods.length];
for (int i = 0; i < methods.length; i++) {
methSigs[i] = new MemberSignature(methods[i]);
}
Arrays.sort(methSigs, new Comparator() {
public int compare(MemberSignature ms1, MemberSignature ms2) {
int comp = ms1.name.compareTo(ms2.name);
if (comp == 0) {
comp = ms1.signature.compareTo(ms2.signature);
}
return comp;
}
});
for (int i = 0; i < methSigs.length; i++) {
MemberSignature sig = methSigs[i];
int mods = sig.member.getModifiers() &
(Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
Modifier.STATIC | Modifier.FINAL |
Modifier.SYNCHRONIZED | Modifier.NATIVE |
Modifier.ABSTRACT | Modifier.STRICT);
if ((mods & Modifier.PRIVATE) == 0) {
dout.writeUTF(sig.name);
dout.writeInt(mods);
dout.writeUTF(sig.signature.replace('/', '.'));
}
}
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] hashBytes = md.digest(bout.toByteArray());
long hash = 0;
for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
hash = (hash << 8) | (hashBytes[i] & 0xFF);
}
return hash;
/** serialPersistentFields value indicating no serializable fields */
public static final ObjectStreamField[] NO_FIELDS =
new ObjectStreamField[0];
private static final ObjectStreamField[] serialPersistentFields =
NO_FIELDS;
private static ObjectStreamField[] getDeclaredSerialFields(Class> cl)
throws InvalidClassException
{
ObjectStreamField[] serialPersistentFields = null;
try {
Field f = cl.getDeclaredField("serialPersistentFields");
int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
if ((f.getModifiers() & mask) == mask) {
f.setAccessible(true);
serialPersistentFields = (ObjectStreamField[]) f.get(null);
}
} catch (Exception ex) {
}
if (serialPersistentFields == null) {
return null;
} else if (serialPersistentFields.length == 0) {
return NO_FIELDS;
}
ObjectStreamField[] boundFields =
new ObjectStreamField[serialPersistentFields.length];
Set fieldNames = new HashSet<>(serialPersistentFields.length);
for (int i = 0; i < serialPersistentFields.length; i++) {
ObjectStreamField spf = serialPersistentFields[i];
String fname = spf.getName();
if (fieldNames.contains(fname)) {
throw new InvalidClassException(
"multiple serializable fields named " + fname);
}
fieldNames.add(fname);
try {
Field f = cl.getDeclaredField(fname);
if ((f.getType() == spf.getType()) &&
((f.getModifiers() & Modifier.STATIC) == 0))
{
boundFields[i] =
new ObjectStreamField(f, spf.isUnshared(), true);
}
} catch (NoSuchFieldException ex) {
}
if (boundFields[i] == null) {
boundFields[i] = new ObjectStreamField(
fname, spf.getType(), spf.isUnshared());
}
}
return boundFields;
}
private static ObjectStreamField[] getDefaultSerialFields(Class> cl) {
Field[] clFields = cl.getDeclaredFields();
ArrayList list = new ArrayList<>();
int mask = Modifier.STATIC | Modifier.TRANSIENT;
for (int i = 0; i < clFields.length; i++) {
if ((clFields[i].getModifiers() & mask) == 0) {
list.add(new ObjectStreamField(clFields[i], false, true));
}
}
int size = list.size();
return (size == 0) ? NO_FIELDS :
list.toArray(new ObjectStreamField[size]);
}
private static ObjectStreamField[] getSerialFields(Class> cl)
throws InvalidClassException
{
ObjectStreamField[] fields;
if (Serializable.class.isAssignableFrom(cl) &&
!Externalizable.class.isAssignableFrom(cl) &&
!Proxy.isProxyClass(cl) &&
!cl.isInterface())
{
if ((fields = getDeclaredSerialFields(cl)) == null) {
fields = getDefaultSerialFields(cl);
}
Arrays.sort(fields);
} else {
fields = NO_FIELDS;
}
return fields;
}
new ObjectStreamField(f, spf.isUnshared(), true);
new ObjectStreamField(clFields[i], false, true)
ObjectStreamField[] getFields(boolean copy) {
return copy ? fields.clone() : fields;
}
ObjectStreamField getField(String name, Class> type) {
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
if (f.getName().equals(name)) {
if (type == null ||
(type == Object.class && !f.isPrimitive()))
{
return f;
}
Class> ftype = f.getType();
if (ftype != null && type.isAssignableFrom(ftype)) {
return f;
}
}
}
return null;
}
public ObjectStreamField[] getFields() {
return getFields(true);
}
public ObjectStreamField getField(String name) {
return getField(name, null);
}
上述字段中获取被解析类的字段方法分为两种类型:getFields和getField方法,这两个方法都各自有一个重载方法;
void getPrimFieldValues(Object obj, byte[] buf) {
fieldRefl.getPrimFieldValues(obj, buf);
}
void setPrimFieldValues(Object obj, byte[] buf) {
fieldRefl.setPrimFieldValues(obj, buf);
}
void getObjFieldValues(Object obj, Object[] vals) {
fieldRefl.getObjFieldValues(obj, vals);
}
void setObjFieldValues(Object obj, Object[] vals) {
fieldRefl.setObjFieldValues(obj, vals);
}
/** class associated with this descriptor (if any) */
private Class> cl;
/** name of class represented by this descriptor */
private String name;
/** serialVersionUID of represented class (null if not computed yet) */
private volatile Long suid;
/** true if represents dynamic proxy class */
private boolean isProxy;
/** true if represents enum type */
private boolean isEnum;
/** true if represented class implements Serializable */
private boolean serializable;
/** true if represented class implements Externalizable */
private boolean externalizable;
/** true if desc has data written by class-defined writeObject method */
private boolean hasWriteObjectData;
/**
* true if desc has externalizable data written in block data format; this
* must be true by default to accommodate ObjectInputStream subclasses which
* override readClassDescriptor() to return class descriptors obtained from
* ObjectStreamClass.lookup() (see 4461737)
*/
private boolean hasBlockExternalData = true;
/** exception (if any) thrown while attempting to resolve class */
private ClassNotFoundException resolveEx;
/** exception (if any) to throw if non-enum deserialization attempted */
private ExceptionInfo deserializeEx;
/** exception (if any) to throw if non-enum serialization attempted */
private ExceptionInfo serializeEx;
/** exception (if any) to throw if default serialization attempted */
private ExceptionInfo defaultSerializeEx;
/** serializable fields */
private ObjectStreamField[] fields;
/** aggregate marshalled size of primitive fields */
private int primDataSize;
/** number of non-primitive fields */
private int numObjFields;
/** reflector for setting/getting serializable field values */
private FieldReflector fieldRefl;
/** data layout of serialized objects described by this class desc */
private volatile ClassDataSlot[] dataLayout;
/** serialization-appropriate constructor, or null if none */
private Constructor cons;
/** class-defined writeObject method, or null if none */
private Method writeObjectMethod;
/** class-defined readObject method, or null if none */
private Method readObjectMethod;
/** class-defined readObjectNoData method, or null if none */
private Method readObjectNoDataMethod;
/** class-defined writeReplace method, or null if none */
private Method writeReplaceMethod;
/** class-defined readResolve method, or null if none */
private Method readResolveMethod;
/** local class descriptor for represented class (may point to self) */
private ObjectStreamClass localDesc;
/** superclass descriptor appearing in stream */
private ObjectStreamClass superDesc;
public String getName() {
return name;
}
public Class> forClass() {
return cl;
}
ObjectStreamClass getSuperDesc() {
return superDesc;
}
ObjectStreamClass getLocalDesc() {
return localDesc;
}
boolean isProxy() {
return isProxy;
}
boolean isEnum() {
return isEnum;
}
boolean isExternalizable() {
return externalizable;
}
boolean isSerializable() {
return serializable;
}
boolean hasBlockExternalData() {
return hasBlockExternalData;
}
boolean hasWriteObjectData() {
return hasWriteObjectData;
}
boolean hasWriteObjectMethod() {
return (writeObjectMethod != null);
}
boolean hasReadObjectMethod() {
return (readObjectMethod != null);
}
boolean hasReadObjectNoDataMethod() {
return (readObjectNoDataMethod != null);
}
boolean hasWriteReplaceMethod() {
return (writeReplaceMethod != null);
}
boolean hasReadResolveMethod() {
return (readResolveMethod != null);
}
boolean isInstantiable() {
return (cons != null);
}
int getPrimDataSize() {
return primDataSize;
}
int getNumObjFields() {
return numObjFields;
}
ClassNotFoundException getResolveException() {
return resolveEx;
}
void checkDeserialize() throws InvalidClassException {
if (deserializeEx != null) {
throw deserializeEx.newInvalidClassException();
}
}
void checkSerialize() throws InvalidClassException {
if (serializeEx != null) {
throw serializeEx.newInvalidClassException();
}
}
void checkDefaultSerialize() throws InvalidClassException {
if (defaultSerializeEx != null) {
throw defaultSerializeEx.newInvalidClassException();
}
}
private static Constructor getExternalizableConstructor(Class> cl) {
try {
Constructor cons = cl.getDeclaredConstructor((Class>[]) null);
cons.setAccessible(true);
return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
cons : null;
} catch (NoSuchMethodException ex) {
return null;
}
}
private static Constructor getSerializableConstructor(Class> cl) {
Class> initCl = cl;
while (Serializable.class.isAssignableFrom(initCl)) {
if ((initCl = initCl.getSuperclass()) == null) {
return null;
}
}
try {
Constructor cons = initCl.getDeclaredConstructor((Class>[]) null);
int mods = cons.getModifiers();
if ((mods & Modifier.PRIVATE) != 0 ||
((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
!packageEquals(cl, initCl)))
{
return null;
}
cons = reflFactory.newConstructorForSerialization(cl, cons);
cons.setAccessible(true);
return cons;
} catch (NoSuchMethodException ex) {
return null;
}
}
/** reflection factory for obtaining serialization constructors */
private static final ReflectionFactory reflFactory =
AccessController.doPrivileged(
new ReflectionFactory.GetReflectionFactoryAction());
private static Method getInheritableMethod(Class> cl, String name,
Class>[] argTypes,
Class> returnType)
{
Method meth = null;
Class> defCl = cl;
while (defCl != null) {
try {
meth = defCl.getDeclaredMethod(name, argTypes);
break;
} catch (NoSuchMethodException ex) {
defCl = defCl.getSuperclass();
}
}
if ((meth == null) || (meth.getReturnType() != returnType)) {
return null;
}
meth.setAccessible(true);
int mods = meth.getModifiers();
if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
return null;
} else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
return meth;
} else if ((mods & Modifier.PRIVATE) != 0) {
return (cl == defCl) ? meth : null;
} else {
return packageEquals(cl, defCl) ? meth : null;
}
}
private static Method getPrivateMethod(Class> cl, String name,
Class>[] argTypes,
Class> returnType)
{
try {
Method meth = cl.getDeclaredMethod(name, argTypes);
meth.setAccessible(true);
int mods = meth.getModifiers();
return ((meth.getReturnType() == returnType) &&
((mods & Modifier.STATIC) == 0) &&
((mods & Modifier.PRIVATE) != 0)) ? meth : null;
} catch (NoSuchMethodException ex) {
return null;
}
}
void invokeWriteObject(Object obj, ObjectOutputStream out)
throws IOException, UnsupportedOperationException
{
if (writeObjectMethod != null) {
try {
writeObjectMethod.invoke(obj, new Object[]{ out });
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof IOException) {
throw (IOException) th;
} else {
throwMiscException(th);
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
void invokeReadObject(Object obj, ObjectInputStream in)
throws ClassNotFoundException, IOException,
UnsupportedOperationException
{
if (readObjectMethod != null) {
try {
readObjectMethod.invoke(obj, new Object[]{ in });
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ClassNotFoundException) {
throw (ClassNotFoundException) th;
} else if (th instanceof IOException) {
throw (IOException) th;
} else {
throwMiscException(th);
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
void invokeReadObjectNoData(Object obj)
throws IOException, UnsupportedOperationException
{
if (readObjectNoDataMethod != null) {
try {
readObjectNoDataMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
Object invokeWriteReplace(Object obj)
throws IOException, UnsupportedOperationException
{
if (writeReplaceMethod != null) {
try {
return writeReplaceMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
Object invokeReadResolve(Object obj)
throws IOException, UnsupportedOperationException
{
if (readResolveMethod != null) {
try {
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
static void processQueue(ReferenceQueue> queue,
ConcurrentMap extends
WeakReference>, ?> map)
{
Reference extends Class>> ref;
while((ref = queue.poll()) != null) {
map.remove(ref);
}
}
private static void throwMiscException(Throwable th) throws IOException {
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else if (th instanceof Error) {
throw (Error) th;
} else {
IOException ex = new IOException("unexpected exception type");
ex.initCause(th);
throw ex;
}
}
private static boolean classNamesEqual(String name1, String name2) {
name1 = name1.substring(name1.lastIndexOf('.') + 1);
name2 = name2.substring(name2.lastIndexOf('.') + 1);
return name1.equals(name2);
}
private static boolean packageEquals(Class> cl1, Class> cl2) {
return (cl1.getClassLoader() == cl2.getClassLoader() &&
getPackageName(cl1).equals(getPackageName(cl2)));
}
private static String getPackageName(Class> cl) {
String s = cl.getName();
int i = s.lastIndexOf('[');
if (i >= 0) {
s = s.substring(i + 2);
}
i = s.lastIndexOf('.');
return (i >= 0) ? s.substring(0, i) : "";
}
private void computeFieldOffsets() throws InvalidClassException {
primDataSize = 0;
numObjFields = 0;
int firstObjIndex = -1;
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
switch (f.getTypeCode()) {
case 'Z':
case 'B':
f.setOffset(primDataSize++);
break;
case 'C':
case 'S':
f.setOffset(primDataSize);
primDataSize += 2;
break;
case 'I':
case 'F':
f.setOffset(primDataSize);
primDataSize += 4;
break;
case 'J':
case 'D':
f.setOffset(primDataSize);
primDataSize += 8;
break;
case '[':
case 'L':
f.setOffset(numObjFields++);
if (firstObjIndex == -1) {
firstObjIndex = i;
}
break;
default:
throw new InternalError();
}
}
if (firstObjIndex != -1 &&
firstObjIndex + numObjFields != fields.length)
{
throw new InvalidClassException(name, "illegal field order");
}
}
public String toString() {
return name + ": static final long serialVersionUID = " +
getSerialVersionUID() + "L;";
}
private ObjectStreamClass getVariantFor(Class> cl)
throws InvalidClassException
{
if (this.cl == cl) {
return this;
}
ObjectStreamClass desc = new ObjectStreamClass();
if (isProxy) {
desc.initProxy(cl, null, superDesc);
} else {
desc.initNonProxy(this, cl, null, superDesc);
}
return desc;
}
private static FieldReflector getReflector(ObjectStreamField[] fields,
ObjectStreamClass localDesc)
throws InvalidClassException
{
// class irrelevant if no fields
Class> cl = (localDesc != null && fields.length > 0) ?
localDesc.cl : null;
processQueue(Caches.reflectorsQueue, Caches.reflectors);
FieldReflectorKey key = new FieldReflectorKey(cl, fields,
Caches.reflectorsQueue);
Reference> ref = Caches.reflectors.get(key);
Object entry = null;
if (ref != null) {
entry = ref.get();
}
EntryFuture future = null;
if (entry == null) {
EntryFuture newEntry = new EntryFuture();
Reference> newRef = new SoftReference<>(newEntry);
do {
if (ref != null) {
Caches.reflectors.remove(key, ref);
}
ref = Caches.reflectors.putIfAbsent(key, newRef);
if (ref != null) {
entry = ref.get();
}
} while (ref != null && entry == null);
if (entry == null) {
future = newEntry;
}
}
if (entry instanceof FieldReflector) { // check common case first
return (FieldReflector) entry;
} else if (entry instanceof EntryFuture) {
entry = ((EntryFuture) entry).get();
} else if (entry == null) {
try {
entry = new FieldReflector(matchFields(fields, localDesc));
} catch (Throwable th) {
entry = th;
}
future.set(entry);
Caches.reflectors.put(key, new SoftReference
private static ObjectStreamField[] matchFields(ObjectStreamField[] fields,
ObjectStreamClass localDesc)
throws InvalidClassException
{
ObjectStreamField[] localFields = (localDesc != null) ?
localDesc.fields : NO_FIELDS;
/*
* Even if fields == localFields, we cannot simply return localFields
* here. In previous implementations of serialization,
* ObjectStreamField.getType() returned Object.class if the
* ObjectStreamField represented a non-primitive field and belonged to
* a non-local class descriptor. To preserve this (questionable)
* behavior, the ObjectStreamField instances returned by matchFields
* cannot report non-primitive types other than Object.class; hence
* localFields cannot be returned directly.
*/
ObjectStreamField[] matches = new ObjectStreamField[fields.length];
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i], m = null;
for (int j = 0; j < localFields.length; j++) {
ObjectStreamField lf = localFields[j];
if (f.getName().equals(lf.getName())) {
if ((f.isPrimitive() || lf.isPrimitive()) &&
f.getTypeCode() != lf.getTypeCode())
{
throw new InvalidClassException(localDesc.name,
"incompatible types for field " + f.getName());
}
if (lf.getField() != null) {
m = new ObjectStreamField(
lf.getField(), lf.isUnshared(), false);
} else {
m = new ObjectStreamField(
lf.getName(), lf.getSignature(), lf.isUnshared());
}
}
}
if (m == null) {
m = new ObjectStreamField(
f.getName(), f.getSignature(), false);
}
m.setOffset(f.getOffset());
matches[i] = m;
}
return matches;
}
Object newInstance()
throws InstantiationException, InvocationTargetException,
UnsupportedOperationException
{
if (cons != null) {
try {
return cons.newInstance();
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError();
}
} else {
throw new UnsupportedOperationException();
}
}
ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
// REMIND: synchronize instead of relying on volatile?
if (dataLayout == null) {
dataLayout = getClassDataLayout0();
}
return dataLayout;
}
private ClassDataSlot[] getClassDataLayout0()
throws InvalidClassException
{
ArrayList slots = new ArrayList<>();
Class> start = cl, end = cl;
// locate closest non-serializable superclass
while (end != null && Serializable.class.isAssignableFrom(end)) {
end = end.getSuperclass();
}
for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
// search up inheritance hierarchy for class with matching name
String searchName = (d.cl != null) ? d.cl.getName() : d.name;
Class> match = null;
for (Class> c = start; c != end; c = c.getSuperclass()) {
if (searchName.equals(c.getName())) {
match = c;
break;
}
}
// add "no data" slot for each unmatched class below match
if (match != null) {
for (Class> c = start; c != match; c = c.getSuperclass()) {
slots.add(new ClassDataSlot(
ObjectStreamClass.lookup(c, true), false));
}
start = match.getSuperclass();
}
// record descriptor/class pairing
slots.add(new ClassDataSlot(d.getVariantFor(match), true));
}
// add "no data" slot for any leftover unmatched classes
for (Class> c = start; c != end; c = c.getSuperclass()) {
slots.add(new ClassDataSlot(
ObjectStreamClass.lookup(c, true), false));
}
// order slots from superclass -> subclass
Collections.reverse(slots);
return slots.toArray(new ClassDataSlot[slots.size()]);
}
public static ObjectStreamClass lookup(Class> cl) {
return lookup(cl, false);
}
public static ObjectStreamClass lookupAny(Class> cl) {
return lookup(cl, true);
}
static ObjectStreamClass lookup(Class> cl, boolean all) {
if (!(all || Serializable.class.isAssignableFrom(cl))) {
return null;
}
processQueue(Caches.localDescsQueue, Caches.localDescs);
WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
Reference> ref = Caches.localDescs.get(key);
Object entry = null;
if (ref != null) {
entry = ref.get();
}
EntryFuture future = null;
if (entry == null) {
EntryFuture newEntry = new EntryFuture();
Reference> newRef = new SoftReference<>(newEntry);
do {
if (ref != null) {
Caches.localDescs.remove(key, ref);
}
ref = Caches.localDescs.putIfAbsent(key, newRef);
if (ref != null) {
entry = ref.get();
}
} while (ref != null && entry == null);
if (entry == null) {
future = newEntry;
}
}
if (entry instanceof ObjectStreamClass) { // check common case first
return (ObjectStreamClass) entry;
}
if (entry instanceof EntryFuture) {
future = (EntryFuture) entry;
if (future.getOwner() == Thread.currentThread()) {
/*
* Handle nested call situation described by 4803747: waiting
* for future value to be set by a lookup() call further up the
* stack will result in deadlock, so calculate and set the
* future value here instead.
*/
entry = null;
} else {
entry = future.get();
}
}
if (entry == null) {
try {
entry = new ObjectStreamClass(cl);
} catch (Throwable th) {
entry = th;
}
if (future.set(entry)) {
Caches.localDescs.put(key, new SoftReference
if (!(all || Serializable.class.isAssignableFrom(cl))) {
return null;
}
processQueue(Caches.localDescsQueue, Caches.localDescs);
WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
Reference> ref = Caches.localDescs.get(key);
Object entry = null;
if (ref != null) {
entry = ref.get();
}
EntryFuture future = null;
if (entry == null) {
EntryFuture newEntry = new EntryFuture();
Reference> newRef = new SoftReference<>(newEntry);
do {
if (ref != null) {
Caches.localDescs.remove(key, ref);
}
ref = Caches.localDescs.putIfAbsent(key, newRef);
if (ref != null) {
entry = ref.get();
}
} while (ref != null && entry == null);
if (entry == null) {
future = newEntry;
}
}
if (entry instanceof ObjectStreamClass) { // check common case first
return (ObjectStreamClass) entry;
}
if (entry instanceof EntryFuture) {
future = (EntryFuture) entry;
if (future.getOwner() == Thread.currentThread()) {
/*
* Handle nested call situation described by 4803747: waiting
* for future value to be set by a lookup() call further up the
* stack will result in deadlock, so calculate and set the
* future value here instead.
*/
entry = null;
} else {
entry = future.get();
}
}
if (entry == null) {
try {
entry = new ObjectStreamClass(cl);
} catch (Throwable th) {
entry = th;
}
if (future.set(entry)) {
Caches.localDescs.put(key, new SoftReference
if (entry instanceof ObjectStreamClass) {
return (ObjectStreamClass) entry;
} else if (entry instanceof RuntimeException) {
throw (RuntimeException) entry;
} else if (entry instanceof Error) {
throw (Error) entry;
} else {
throw new InternalError("unexpected entry: " + entry);
}
private ObjectStreamClass(final Class> cl) {
this.cl = cl;
name = cl.getName();
isProxy = Proxy.isProxyClass(cl);
isEnum = Enum.class.isAssignableFrom(cl);
serializable = Serializable.class.isAssignableFrom(cl);
externalizable = Externalizable.class.isAssignableFrom(cl);
Class> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
localDesc = this;
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
if (isEnum) {
suid = Long.valueOf(0);
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
suid = getDeclaredSUID(cl);
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}
});
} else {
suid = Long.valueOf(0);
fields = NO_FIELDS;
}
try {
fieldRefl = getReflector(fields, this);
} catch (InvalidClassException ex) {
// field mismatches impossible when matching local fields vs. self
throw new InternalError();
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
} else if (cons == null) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
for (int i = 0; i < fields.length; i++) {
if (fields[i].getField() == null) {
defaultSerializeEx = new ExceptionInfo(
name, "unmatched serializable field(s) declared");
}
}
}
ObjectStreamClass() {
}
if (isEnum) {
suid = Long.valueOf(0);
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
suid = getDeclaredSUID(cl);
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
从上述代码可以看到,这段代码的设置如下: if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
相信不需要解释上边代码的含义了。代码执行到这里,大部分成员属性的构造初始化过程都完成了; if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
} else if (cons == null) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
for (int i = 0; i < fields.length; i++) {
if (fields[i].getField() == null) {
defaultSerializeEx = new ExceptionInfo(
name, "unmatched serializable field(s) declared");
}
}
前文中虽然讲过几个异常信息类,这里先看看第5步解析的代码中的catch块的内容: serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
从上边这段代码中可以看到serializeEx和deserializeEx会在获取被解析类的可序列化字段元数据过程中因为出现异常而设置;其实isEnum为空的语句是一句“防御式”的编程,在前边调用过程如果isEnum判断为真则serializeEx和deserializeEx都会为null,而这里再作判断的情况可能就属于十分稀有的情况了; void initProxy(Class> cl,
ClassNotFoundException resolveEx,
ObjectStreamClass superDesc)
throws InvalidClassException
{
this.cl = cl;
this.resolveEx = resolveEx;
this.superDesc = superDesc;
isProxy = true;
serializable = true;
suid = Long.valueOf(0);
fields = NO_FIELDS;
if (cl != null) {
localDesc = lookup(cl, true);
if (!localDesc.isProxy) {
throw new InvalidClassException(
"cannot bind proxy descriptor to a non-proxy class");
}
name = localDesc.name;
externalizable = localDesc.externalizable;
cons = localDesc.cons;
writeReplaceMethod = localDesc.writeReplaceMethod;
readResolveMethod = localDesc.readResolveMethod;
deserializeEx = localDesc.deserializeEx;
}
fieldRefl = getReflector(fields, localDesc);
}
void initNonProxy(ObjectStreamClass model,
Class> cl,
ClassNotFoundException resolveEx,
ObjectStreamClass superDesc)
throws InvalidClassException
{
this.cl = cl;
this.resolveEx = resolveEx;
this.superDesc = superDesc;
name = model.name;
suid = Long.valueOf(model.getSerialVersionUID());
isProxy = false;
isEnum = model.isEnum;
serializable = model.serializable;
externalizable = model.externalizable;
hasBlockExternalData = model.hasBlockExternalData;
hasWriteObjectData = model.hasWriteObjectData;
fields = model.fields;
primDataSize = model.primDataSize;
numObjFields = model.numObjFields;
if (cl != null) {
localDesc = lookup(cl, true);
if (localDesc.isProxy) {
throw new InvalidClassException(
"cannot bind non-proxy descriptor to a proxy class");
}
if (isEnum != localDesc.isEnum) {
throw new InvalidClassException(isEnum ?
"cannot bind enum descriptor to a non-enum class" :
"cannot bind non-enum descriptor to an enum class");
}
if (serializable == localDesc.serializable &&
!cl.isArray() &&
suid.longValue() != localDesc.getSerialVersionUID())
{
throw new InvalidClassException(localDesc.name,
"local class incompatible: " +
"stream classdesc serialVersionUID = " + suid +
", local class serialVersionUID = " +
localDesc.getSerialVersionUID());
}
if (!classNamesEqual(name, localDesc.name)) {
throw new InvalidClassException(localDesc.name,
"local class name incompatible with stream class " +
"name \"" + name + "\"");
}
if (!isEnum) {
if ((serializable == localDesc.serializable) &&
(externalizable != localDesc.externalizable))
{
throw new InvalidClassException(localDesc.name,
"Serializable incompatible with Externalizable");
}
if ((serializable != localDesc.serializable) ||
(externalizable != localDesc.externalizable) ||
!(serializable || externalizable))
{
deserializeEx = new ExceptionInfo(
localDesc.name, "class invalid for deserialization");
}
}
cons = localDesc.cons;
writeObjectMethod = localDesc.writeObjectMethod;
readObjectMethod = localDesc.readObjectMethod;
readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
writeReplaceMethod = localDesc.writeReplaceMethod;
readResolveMethod = localDesc.readResolveMethod;
if (deserializeEx == null) {
deserializeEx = localDesc.deserializeEx;
}
}
fieldRefl = getReflector(fields, localDesc);
// reassign to matched fields so as to reflect local unshared settings
fields = fieldRefl.getFields();
}
// reassign to matched fields so as to reflect local unshared settings
fields = fieldRefl.getFields();
void writeNonProxy(ObjectOutputStream out) throws IOException {
out.writeUTF(name);
out.writeLong(getSerialVersionUID());
byte flags = 0;
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
int protocol = out.getProtocolVersion();
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
out.writeByte(flags);
out.writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
out.writeByte(f.getTypeCode());
out.writeUTF(f.getName());
if (!f.isPrimitive()) {
out.writeTypeString(f.getTypeString());
}
}
}
out.writeUTF(name);
out.writeLong(getSerialVersionUID());
byte flags = 0;
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
int protocol = out.getProtocolVersion();
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
out.writeByte(flags);
out.writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
out.writeByte(f.getTypeCode());
out.writeUTF(f.getName());
if (!f.isPrimitive()) {
out.writeTypeString(f.getTypeString());
}
}
void readNonProxy(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
name = in.readUTF();
suid = Long.valueOf(in.readLong());
isProxy = false;
byte flags = in.readByte();
hasWriteObjectData =
((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
hasBlockExternalData =
((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
externalizable =
((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
boolean sflag =
((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
if (externalizable && sflag) {
throw new InvalidClassException(
name, "serializable and externalizable flags conflict");
}
serializable = externalizable || sflag;
isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
if (isEnum && suid.longValue() != 0L) {
throw new InvalidClassException(name,
"enum descriptor has non-zero serialVersionUID: " + suid);
}
int numFields = in.readShort();
if (isEnum && numFields != 0) {
throw new InvalidClassException(name,
"enum descriptor has non-zero field count: " + numFields);
}
fields = (numFields > 0) ?
new ObjectStreamField[numFields] : NO_FIELDS;
for (int i = 0; i < numFields; i++) {
char tcode = (char) in.readByte();
String fname = in.readUTF();
String signature = ((tcode == 'L') || (tcode == '[')) ?
in.readTypeString() : new String(new char[] { tcode });
try {
fields[i] = new ObjectStreamField(fname, signature, false);
} catch (RuntimeException e) {
throw (IOException) new InvalidClassException(name,
"invalid descriptor for field " + fname).initCause(e);
}
}
computeFieldOffsets();
}
name = in.readUTF();
suid = Long.valueOf(in.readLong());
byte flags = in.readByte();
hasWriteObjectData =
((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
hasBlockExternalData =
((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
externalizable =
((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
boolean sflag =
((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
if (externalizable && sflag) {
throw new InvalidClassException(
name, "serializable and externalizable flags conflict");
}
serializable = externalizable || sflag;
isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
if (isEnum && suid.longValue() != 0L) {
throw new InvalidClassException(name,
"enum descriptor has non-zero serialVersionUID: " + suid);
}
int numFields = in.readShort();
if (isEnum && numFields != 0) {
throw new InvalidClassException(name,
"enum descriptor has non-zero field count: " + numFields);
}
fields = (numFields > 0) ?
new ObjectStreamField[numFields] : NO_FIELDS;
for (int i = 0; i < numFields; i++) {
char tcode = (char) in.readByte();
String fname = in.readUTF();
String signature = ((tcode == 'L') || (tcode == '[')) ?
in.readTypeString() : new String(new char[] { tcode });
try {
fields[i] = new ObjectStreamField(fname, signature, false);
} catch (RuntimeException e) {
throw (IOException) new InvalidClassException(name,
"invalid descriptor for field " + fname).initCause(e);
}
}