(从09年回到重庆过后,就一直在工作,时间长了惰性就慢慢起来了,公司的项目从09年忙到了现在,一直没有时间来梳理自己的东西,CSDN的Blog似乎都荒废了,不知道现在还能否坚持把Blog完成,希望有一个新的开始吧!如果读者有问题还是可直接发邮件到[email protected],我也仅仅只是想把看的、写的、学的东西总结起来,让自己有个比较完整的学习记录。本文主要针对Java的序列化相关知识,先不涉及XML序列化和Json序列化的内容,这部分内容以后再议。着色的目的是强调重点和关键的概念以及防止读者通篇阅读的视觉疲劳,也是个人的写作风格,不习惯的读者请见谅!——本来打算把全文的内容放在一篇Blog中来讲解,慢慢地就发现Java序列化的内容其实蛮多的,篇幅长了过后排版慢慢变得比较复杂,写这么多的目的就是为了把我自己理解过、分析过、验证过以及开发的时候用过的内容分享给大家,也许我不能保证在CSDN上频繁地发BLOG,但每发一次尽可能保证这些内容的营养价值。)
本章目录:
1.Java中的序列化
2.序列化原理和算法——基础数据
3.深入序列化规范
4.源码分析
----ObjectStreamField
----ObjectStreamClass
----ObjectOutputStream
----ObjectInputStream
5.序列化原理和算法——面向对象
4.源码分析
在讲解本章的内容之前,先简单回顾一下:前文讲了Java中的序列化的基本概念,并且通过输出的二进制文件内容的分析理解了Java序列化的原理和算法,同样引领读者解析了JVM的序列化规范;第二章中我们分析了ObjectStreamField和ObjectStreamClass两个类的源代码。
其实迄今为止,所有的讲解和分析都是从一个角度在阐述——数据结构。二进制文件内容的分析实际上是让读者去理解Java序列化生成的目标数据是什么结构,而ObjectStreamField和ObjectStreamClass两个类主要用途就是用于描述对象所属类的元数据以及它所定义的成员属性的相关元数据,它们的源码中除了ObjectStreamClass中的readNonProxy和writeNonProxy包含了和序列化和反序列化执行过程的内容以外,其他所有的内容都是和数据结构相关的东西。
前边的章节我们一直讨论了下边几个问题:
iii.ObjectOutputStream源码分析
ObjectOutputStream是Java序列化机制中负责序列化的主类,在使用它的时候会用到前文提及的ObjectStreamClass以及ObjectStreamField两个核心类,接下来我们一步一步去揭开这个类的面纱:
1)类定义:
ObjectOutputStream的完整类定义如下:
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants
从ObjectOutputStream的定义可以知道,它有一个父类OutputStream,实现了两个接口ObjectOutput以及ObjectStreamConstants。Java序列化中包含了很多常量值,其中前文提到的最多的是TC_*标记和SC_*标记,这些标记都是在接口ObjectStreamConstants中定义的,所以它们都属于接口常量。不仅仅是ObjectOutputStream实现了该接口,反序列化的主类ObjectInputStream同样也实现了该接口。父类OutputStream以及接口ObjectOutput的职责如下:
private static class Caches
public static abstract class PutField
private class PutFieldImpl extends PutField
private static class BlockDataOutputStream extends OutputStream implements DataOutput
private static class HandleTable
private static class ReplaceTable
private static class DebugTraceInfoStack
Caches【缓存类】
该类提供了序列化中的缓存机制,它只包含了两个固定的成员属性,其完整定义如下:
private static class Caches {
/** cache of subclass security audit results */
static final ConcurrentMap subclassAudits =
new ConcurrentHashMap<>();
/** queue for WeakReferences to audited subclasses */
static final ReferenceQueue> subclassAuditsQueue =
new ReferenceQueue<>();
}
Caches类中包含了两个成员subclassAudits和subclasseAuditsQueue:
subclassAudits——该成员属性提供了一个哈希表缓存,该缓存的键类型为java.io.ObjectStreamClass.WeakClassKey,注意看它的值类型是一个java.lang.Boolean类型的,从其代码注释可以知道这个哈希表缓存中保存的是所有子类的代码执行安全性检测结果;
subclassAuditsQueue——该成员属性定义了一个“Queue队列”,它的用法和前文中ObjectStreamClass.Caches中“Queue”的用法是一致的;
PutField:
PutField类为一个抽象类,它提供对要写入ObjectOutput的持久字段的编程访问,先看看它的源代码:
public static abstract class PutField {
public abstract void put(String name, boolean val);
public abstract void put(String name, byte val);
public abstract void put(String name, char val);
public abstract void put(String name, short val);
public abstract void put(String name, int val);
public abstract void put(String name, long val);
public abstract void put(String name, float val);
public abstract void put(String name, double val);
public abstract void put(String name, Object val);
@Deprecated
public abstract void write(ObjectOutput out) throws IOException;
}
PutField抽象类中主要有9个put方法,以及一个已经过期的write方法,put方法主要将对应类型的字段值写入到持久化字段中,9个方法分别对应Java语言中的8个基础类型以及1个Object类型的字段的写入。它的参数如下:
/** class descriptor describing serializable fields */
private final ObjectStreamClass desc;
/** primitive field values */
private final byte[] primVals;
/** object field values */
private final Object[] objVals;
PutFieldImpl(ObjectStreamClass desc) {
this.desc = desc;
primVals = new byte[desc.getPrimDataSize()];
objVals = new Object[desc.getNumObjFields()];
}
PutFieldImpl类的构造函数拥有一个参数desc,该参数的类型为java.io.ObjectStreamClass,PutFieldImpl实现类会从该类的元数据中提取成员属性信息;一方面desc引用会赋值该成员属性desc,然后通过类的元数据中的字段信息初始化primVals字节数组以及objVals对象数组。区分ObjectStreamField类和PutFieldImpl类对字段元数据的描述:ObjectStreamField描述的是成员属性的元数据信息【字段定义】,而PutFieldImpl描述的是成员属性的数据信息【字段使用】。
除了已过期的write方法,PutFieldImpl实现类中提供了另外两个新定义的成员函数
writeFields和getFieldOffset:
void writeFields() throws IOException {
bout.write(primVals, 0, primVals.length, false);
ObjectStreamField[] fields = desc.getFields(false);
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"field (class \"" + desc.getName() + "\", name: \"" +
fields[numPrimFields + i].getName() + "\", type: \"" +
fields[numPrimFields + i].getType() + "\")");
}
try {
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
private int getFieldOffset(String name, Class type) {
ObjectStreamField field = desc.getField(name, type);
if (field == null) {
throw new IllegalArgumentException("no such field " + name +
" with type " + type);
}
return field.getOffset();
}
这里不解析write方法的用法,重点看看子类中新定义的两个成员函数:
writeFields
该方法负责将基础类型数据的值和对象类型数据的值写入字节流,看看它的实现细节:
bout.write(primVals, 0, primVals.length, false);
ObjectStreamField[] fields = desc.getFields(false);
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"field (class \"" + desc.getName() + "\", name: \"" +
fields[numPrimFields + i].getName() + "\", type: \"" +
fields[numPrimFields + i].getType() + "\")");
}
try {
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
public void put(String name, boolean val) {
Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
}
public void put(String name, byte val) {
primVals[getFieldOffset(name, Byte.TYPE)] = val;
}
public void put(String name, char val) {
Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
}
public void put(String name, short val) {
Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
}
public void put(String name, int val) {
Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
}
public void put(String name, float val) {
Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
}
public void put(String name, long val) {
Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
}
public void put(String name, double val) {
Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
}
public void put(String name, Object val) {
objVals[getFieldOffset(name, Object.class)] = val;
}
primVals[getFieldOffset(name, Byte.TYPE)] = val;
objVals[getFieldOffset(name, Object.class)] = val;
Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
private final List stack;
private static class DebugTraceInfoStack {
private final List stack;
DebugTraceInfoStack() {
stack = new ArrayList<>();
}
void clear() {
stack.clear();
}
void pop() {
stack.remove(stack.size()-1);
}
void push(String entry) {
stack.add("\t- " + entry);
}
public String toString() {
StringBuilder buffer = new StringBuilder();
if (!stack.isEmpty()) {
for(int i = stack.size(); i > 0; i-- ) {
buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
}
}
return buffer.toString();
}
}
/* number of mappings in table/next available handle */
private int size;
/* size threshold determining when to expand hash spine */
private int threshold;
/* factor for computing size threshold */
private final float loadFactor;
/* maps hash value -> candidate handle value */
private int[] spine;
/* maps handle value -> next candidate handle value */
private int[] next;
/* maps handle value -> associated object */
private Object[] objs;
HandleTable(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
spine = new int[initialCapacity];
next = new int[initialCapacity];
objs = new Object[initialCapacity];
threshold = (int) (initialCapacity * loadFactor);
clear();
}
int assign(Object obj) {
if (size >= next.length) {
growEntries();
}
if (size >= threshold) {
growSpine();
}
insert(obj, size);
return size++;
}
int lookup(Object obj) {
if (size == 0) {
return -1;
}
int index = hash(obj) % spine.length;
for (int i = spine[index]; i >= 0; i = next[i]) {
if (objs[i] == obj) {
return i;
}
}
return -1;
}
void clear() {
Arrays.fill(spine, -1);
Arrays.fill(objs, 0, size, null);
size = 0;
}
int size() {
return size;
}
private void insert(Object obj, int handle) {
int index = hash(obj) % spine.length;
objs[handle] = obj;
next[handle] = spine[index];
spine[index] = handle;
}
private void growSpine() {
spine = new int[(spine.length << 1) + 1];
threshold = (int) (spine.length * loadFactor);
Arrays.fill(spine, -1);
for (int i = 0; i < size; i++) {
insert(objs[i], i);
}
}
private void growEntries() {
int newLength = (next.length << 1) + 1;
int[] newNext = new int[newLength];
System.arraycopy(next, 0, newNext, 0, size);
next = newNext;
Object[] newObjs = new Object[newLength];
System.arraycopy(objs, 0, newObjs, 0, size);
objs = newObjs;
}
private int hash(Object obj) {
return System.identityHashCode(obj) & 0x7FFFFFFF;
}
private static class ReplaceTable {
/* maps object -> index */
private final HandleTable htab;
/* maps index -> replacement object */
private Object[] reps;
/**
* Creates new ReplaceTable with given capacity and load factor.
*/
ReplaceTable(int initialCapacity, float loadFactor) {
htab = new HandleTable(initialCapacity, loadFactor);
reps = new Object[initialCapacity];
}
/**
* Enters mapping from object to replacement object.
*/
void assign(Object obj, Object rep) {
int index = htab.assign(obj);
while (index >= reps.length) {
grow();
}
reps[index] = rep;
}
/**
* Looks up and returns replacement for given object. If no
* replacement is found, returns the lookup object itself.
*/
Object lookup(Object obj) {
int index = htab.lookup(obj);
return (index >= 0) ? reps[index] : obj;
}
/**
* Resets table to its initial (empty) state.
*/
void clear() {
Arrays.fill(reps, 0, htab.size(), null);
htab.clear();
}
/**
* Returns the number of mappings currently in table.
*/
int size() {
return htab.size();
}
/**
* Increases table capacity.
*/
private void grow() {
Object[] newReps = new Object[(reps.length << 1) + 1];
System.arraycopy(reps, 0, newReps, 0, reps.length);
reps = newReps;
}
}
private static class BlockDataOutputStream extends OutputStream implements DataOutput
/** maximum data block length */
private static final int MAX_BLOCK_SIZE = 1024;
/** maximum data block header length */
private static final int MAX_HEADER_SIZE = 5;
/** (tunable) length of char buffer (for writing strings) */
private static final int CHAR_BUF_SIZE = 256;
/** buffer for writing general/block data */
private final byte[] buf = new byte[MAX_BLOCK_SIZE];
/** buffer for writing block data headers */
private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
/** char buffer for fast string writes */
private final char[] cbuf = new char[CHAR_BUF_SIZE];
/** block data mode */
private boolean blkmode = false;
/** current offset into buf */
private int pos = 0;
/** underlying output stream */
private final OutputStream out;
/** loopback stream (for data writes that span data blocks) */
private final DataOutputStream dout;
BlockDataOutputStream(OutputStream out) {
this.out = out;
dout = new DataOutputStream(this);
}
boolean setBlockDataMode(boolean mode) throws IOException {
if (blkmode == mode) {
return blkmode;
}
drain();
blkmode = mode;
return !blkmode;
}
boolean getBlockDataMode() {
return blkmode;
}
上边两个成员方法用于设置Data Block模式以及获取Data Block模式,看看代码细节,关于Data Block模式的设置中,如果当前模式和传入模式是相等的则不需要设置。
-----------------DataOutput接口方法-------------
DataOutput主要用于将基础数据转换成字节数据写入,该接口的整体信息如下:
因为BlockDataOutputStream实现了这些方法,所以这里先看看这些方法的实现细节:
write方法
该类中包含了三个write的基本方法:
public void write(int b) throws IOException {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
buf[pos++] = (byte) b;
}
public void write(byte[] b) throws IOException {
write(b, 0, b.length, false);
}
public void write(byte[] b, int off, int len) throws IOException {
write(b, off, len, false);
}
上边这三个基本方法主要用于写入字节,除开这三个方法的基本写入以外,该类中还定义了另外一个write方法,该方法是write方法的重载,其定义如下:
void write(byte[] b, int off, int len, boolean copy)
throws IOException
{
if (!(copy || blkmode)) { // write directly
drain();
out.write(b, off, len);
return;
}
while (len > 0) {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
// avoid unnecessary copy
writeBlockHeader(MAX_BLOCK_SIZE);
out.write(b, off, MAX_BLOCK_SIZE);
off += MAX_BLOCK_SIZE;
len -= MAX_BLOCK_SIZE;
} else {
int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
System.arraycopy(b, off, buf, pos, wlen);
pos += wlen;
off += wlen;
len -= wlen;
}
}
}
write*方法
——基础类型写入
public void writeBoolean(boolean v) throws IOException {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
Bits.putBoolean(buf, pos++, v);
}
public void writeByte(int v) throws IOException {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
buf[pos++] = (byte) v;
}
public void writeChar(int v) throws IOException {
if (pos + 2 <= MAX_BLOCK_SIZE) {
Bits.putChar(buf, pos, (char) v);
pos += 2;
} else {
dout.writeChar(v);
}
}
public void writeShort(int v) throws IOException {
if (pos + 2 <= MAX_BLOCK_SIZE) {
Bits.putShort(buf, pos, (short) v);
pos += 2;
} else {
dout.writeShort(v);
}
}
public void writeInt(int v) throws IOException {
if (pos + 4 <= MAX_BLOCK_SIZE) {
Bits.putInt(buf, pos, v);
pos += 4;
} else {
dout.writeInt(v);
}
}
public void writeFloat(float v) throws IOException {
if (pos + 4 <= MAX_BLOCK_SIZE) {
Bits.putFloat(buf, pos, v);
pos += 4;
} else {
dout.writeFloat(v);
}
}
public void writeLong(long v) throws IOException {
if (pos + 8 <= MAX_BLOCK_SIZE) {
Bits.putLong(buf, pos, v);
pos += 8;
} else {
dout.writeLong(v);
}
}
public void writeDouble(double v) throws IOException {
if (pos + 8 <= MAX_BLOCK_SIZE) {
Bits.putDouble(buf, pos, v);
pos += 8;
} else {
dout.writeDouble(v);
}
}
上边三个方法分别负责写入8个基础类型的数据,其写入过程中,注意写入的判断条件:检查其写入过后的偏移量是否越过了Data Block的最大值MAX_BLOCK_SIZE,如果越过了直接调用dout的write*对应方法,未越过的情况针对不同数据类型分别写入,并且修改缓冲区中的偏移量——注:每写入一个数据其偏移量的改变值会根据数据类型来,所以pos += 后边的数字为传入数据类型对应所占字节数;
——String写入
public void writeBytes(String s) throws IOException {
int endoff = s.length();
int cpos = 0;
int csize = 0;
for (int off = 0; off < endoff; ) {
if (cpos >= csize) {
cpos = 0;
csize = Math.min(endoff - off, CHAR_BUF_SIZE);
s.getChars(off, off + csize, cbuf, 0);
}
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
int stop = pos + n;
while (pos < stop) {
buf[pos++] = (byte) cbuf[cpos++];
}
off += n;
}
}
public void writeChars(String s) throws IOException {
int endoff = s.length();
for (int off = 0; off < endoff; ) {
int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
s.getChars(off, off + csize, cbuf, 0);
writeChars(cbuf, 0, csize);
off += csize;
}
}
public void writeUTF(String s) throws IOException {
writeUTF(s, getUTFLength(s));
}
String写入的成员函数主要用于写入一个String对象,在写入String的过程中,同样可以使用三种方式写入String对象到一个字节流:字节方式、字符方式、UTF字符串方式;
-----------------额外定义的批量写入-------------
——基础类型数组写入
这种方式可批量写入基础类型的数据,一般其传入参数为一个数组对象:
void writeBooleans(boolean[] v, int off, int len) throws IOException {
int endoff = off + len;
while (off < endoff) {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
while (off < stop) {
Bits.putBoolean(buf, pos++, v[off++]);
}
}
}
void writeChars(char[] v, int off, int len) throws IOException {
int limit = MAX_BLOCK_SIZE - 2;
int endoff = off + len;
while (off < endoff) {
if (pos <= limit) {
int avail = (MAX_BLOCK_SIZE - pos) >> 1;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putChar(buf, pos, v[off++]);
pos += 2;
}
} else {
dout.writeChar(v[off++]);
}
}
}
void writeShorts(short[] v, int off, int len) throws IOException {
int limit = MAX_BLOCK_SIZE - 2;
int endoff = off + len;
while (off < endoff) {
if (pos <= limit) {
int avail = (MAX_BLOCK_SIZE - pos) >> 1;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putShort(buf, pos, v[off++]);
pos += 2;
}
} else {
dout.writeShort(v[off++]);
}
}
}
void writeInts(int[] v, int off, int len) throws IOException {
int limit = MAX_BLOCK_SIZE - 4;
int endoff = off + len;
while (off < endoff) {
if (pos <= limit) {
int avail = (MAX_BLOCK_SIZE - pos) >> 2;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putInt(buf, pos, v[off++]);
pos += 4;
}
} else {
dout.writeInt(v[off++]);
}
}
}
void writeFloats(float[] v, int off, int len) throws IOException {
int limit = MAX_BLOCK_SIZE - 4;
int endoff = off + len;
while (off < endoff) {
if (pos <= limit) {
int avail = (MAX_BLOCK_SIZE - pos) >> 2;
int chunklen = Math.min(endoff - off, avail);
floatsToBytes(v, off, buf, pos, chunklen);
off += chunklen;
pos += chunklen << 2;
} else {
dout.writeFloat(v[off++]);
}
}
}
void writeLongs(long[] v, int off, int len) throws IOException {
int limit = MAX_BLOCK_SIZE - 8;
int endoff = off + len;
while (off < endoff) {
if (pos <= limit) {
int avail = (MAX_BLOCK_SIZE - pos) >> 3;
int stop = Math.min(endoff, off + avail);
while (off < stop) {
Bits.putLong(buf, pos, v[off++]);
pos += 8;
}
} else {
dout.writeLong(v[off++]);
}
}
}
void writeDoubles(double[] v, int off, int len) throws IOException {
int limit = MAX_BLOCK_SIZE - 8;
int endoff = off + len;
while (off < endoff) {
if (pos <= limit) {
int avail = (MAX_BLOCK_SIZE - pos) >> 3;
int chunklen = Math.min(endoff - off, avail);
doublesToBytes(v, off, buf, pos, chunklen);
off += chunklen;
pos += chunklen << 3;
} else {
dout.writeDouble(v[off++]);
}
}
}
其中批量写入的方式和基础数据写入只有一个区别,因为前面的write方法本身就支持字节数组的写入,所以批量写入的方法定义中没有writeBytes方法。
-----------------String类型的辅助处理-------------
这些辅助方法后边有需要再来详解,目前先提供其代码定义:
long getUTFLength(String s) {
int len = s.length();
long utflen = 0;
for (int off = 0; off < len; ) {
int csize = Math.min(len - off, CHAR_BUF_SIZE);
s.getChars(off, off + csize, cbuf, 0);
for (int cpos = 0; cpos < csize; cpos++) {
char c = cbuf[cpos];
if (c >= 0x0001 && c <= 0x007F) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
}
off += csize;
}
return utflen;
}
void writeUTF(String s, long utflen) throws IOException {
if (utflen > 0xFFFFL) {
throw new UTFDataFormatException();
}
writeShort((int) utflen);
if (utflen == (long) s.length()) {
writeBytes(s);
} else {
writeUTFBody(s);
}
}
void writeLongUTF(String s) throws IOException {
writeLongUTF(s, getUTFLength(s));
}
void writeLongUTF(String s, long utflen) throws IOException {
writeLong(utflen);
if (utflen == (long) s.length()) {
writeBytes(s);
} else {
writeUTFBody(s);
}
}
private void writeUTFBody(String s) throws IOException {
int limit = MAX_BLOCK_SIZE - 3;
int len = s.length();
for (int off = 0; off < len; ) {
int csize = Math.min(len - off, CHAR_BUF_SIZE);
s.getChars(off, off + csize, cbuf, 0);
for (int cpos = 0; cpos < csize; cpos++) {
char c = cbuf[cpos];
if (pos <= limit) {
if (c <= 0x007F && c != 0) {
buf[pos++] = (byte) c;
} else if (c > 0x07FF) {
buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
pos += 3;
} else {
buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
pos += 2;
}
} else { // write one byte at a time to normalize block
if (c <= 0x007F && c != 0) {
write(c);
} else if (c > 0x07FF) {
write(0xE0 | ((c >> 12) & 0x0F));
write(0x80 | ((c >> 6) & 0x3F));
write(0x80 | ((c >> 0) & 0x3F));
} else {
write(0xC0 | ((c >> 6) & 0x1F));
write(0x80 | ((c >> 0) & 0x3F));
}
}
}
off += csize;
}
}
-----------------输出流的辅助处理-------------
实际上从上边的方法定义中可以知道,该类中的大部分方法主要是用于基础类型数据以及String的写入操作,而整个输出流中有4个辅助方法,这四个辅助方法的作用如下:
public void flush() throws IOException {
drain();
out.flush();
}
public void close() throws IOException {
flush();
out.close();
}
void drain() throws IOException {
if (pos == 0) {
return;
}
if (blkmode) {
writeBlockHeader(pos);
}
out.write(buf, 0, pos);
pos = 0;
}
private void writeBlockHeader(int len) throws IOException {
if (len <= 0xFF) {
hbuf[0] = TC_BLOCKDATA;
hbuf[1] = (byte) len;
out.write(hbuf, 0, 2);
} else {
hbuf[0] = TC_BLOCKDATALONG;
Bits.putInt(hbuf, 1, len);
out.write(hbuf, 0, 5);
}
}
到这里所有ObjectOutputStream中的内部类就已经解析完了,接下来看看主类中的信息。
3)成员属性
从前文可以知道,ObjectOutputStream的内部类主要是定义了常用的序列化方法【write*】、需要使用的数据结构【哈希表】以及调试辅助工具,虽然前文并没有解析部分方法的细节内容,但读者可以从其代码实现中自行阅读并且加以理解,在本文后边提供示例的时候,我们会通过代码执行流程来分析序列化的基本步骤,那个时候我会尽可能把前边涉及的内容做一个整合分析。这里我们先来看看ObjectOutputStream类中的成员属性信息:
-----------------哈希表和输出流-------------
/** filter stream for handling block data conversion */
private final BlockDataOutputStream bout;
/** obj -> wire handle map */
private final HandleTable handles;
/** obj -> replacement obj map */
private final ReplaceTable subs;
/** stream protocol version */
private int protocol = PROTOCOL_VERSION_2;
/** recursion depth */
private int depth;
/** buffer for writing primitive field values */
private byte[] primVals;
/** if true, invoke writeObjectOverride() instead of writeObject() */
private final boolean enableOverride;
/** if true, invoke replaceObject() */
private boolean enableReplace;
// values below valid only during upcalls to writeObject()/writeExternal()
/**
* Context during upcalls to class-defined writeObject methods; holds
* object currently being serialized and descriptor for current class.
* Null when not during writeObject upcall.
*/
private SerialCallbackContext curContext;
/** current PutField object */
private PutFieldImpl curPut;
/** custom storage for debug trace info */
private final DebugTraceInfoStack debugInfoStack;
/**
* value of "sun.io.serialization.extendedDebugInfo" property,
* as true or false for extended information about exception's place
*/
private static final boolean extendedDebugInfo =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"sun.io.serialization.extendedDebugInfo")).booleanValue();
/**
* Converts specified span of float values into byte values.
*/
// REMIND: remove once hotspot inlines Float.floatToIntBits
private static native void floatsToBytes(float[] src, int srcpos,
byte[] dst, int dstpos,
int nfloats);
/**
* Converts specified span of double values into byte values.
*/
// REMIND: remove once hotspot inlines Double.doubleToLongBits
private static native void doublesToBytes(double[] src, int srcpos,
byte[] dst, int dstpos,
int ndoubles);
public void write(int val) throws IOException {
bout.write(val);
}
public void write(byte[] buf) throws IOException {
bout.write(buf, 0, buf.length, false);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (buf == null) {
throw new NullPointerException();
}
int endoff = off + len;
if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
throw new IndexOutOfBoundsException();
}
bout.write(buf, off, len, false);
}
public void writeBoolean(boolean val) throws IOException {
bout.writeBoolean(val);
}
public void writeByte(int val) throws IOException {
bout.writeByte(val);
}
public void writeShort(int val) throws IOException {
bout.writeShort(val);
}
public void writeChar(int val) throws IOException {
bout.writeChar(val);
}
public void writeInt(int val) throws IOException {
bout.writeInt(val);
}
public void writeLong(long val) throws IOException {
bout.writeLong(val);
}
public void writeFloat(float val) throws IOException {
bout.writeFloat(val);
}
public void writeDouble(double val) throws IOException {
bout.writeDouble(val);
}
public void writeBytes(String str) throws IOException {
bout.writeBytes(str);
}
public void writeChars(String str) throws IOException {
bout.writeChars(str);
}
public void writeUTF(String str) throws IOException {
bout.writeUTF(str);
}
6)ObjectOutput接口
public void flush() throws IOException {
bout.flush();
}
protected void drain() throws IOException {
bout.drain();
}
public void close() throws IOException {
flush();
clear();
bout.close();
}
public void reset() throws IOException {
if (depth != 0) {
throw new IOException("stream active");
}
bout.setBlockDataMode(false);
bout.writeByte(TC_RESET);
clear();
bout.setBlockDataMode(true);
}
public void useProtocolVersion(int version) throws IOException {
if (handles.size() != 0) {
// REMIND: implement better check for pristine stream?
throw new IllegalStateException("stream non-empty");
}
switch (version) {
case PROTOCOL_VERSION_1:
case PROTOCOL_VERSION_2:
protocol = version;
break;
default:
throw new IllegalArgumentException(
"unknown version: " + version);
}
}
int getProtocolVersion() {
return protocol;
}
public ObjectOutputStream.PutField putFields() throws IOException {
if (curPut == null) {
if (curContext == null) {
throw new NotActiveException("not in call to writeObject");
}
Object curObj = curContext.getObj();
ObjectStreamClass curDesc = curContext.getDesc();
curPut = new PutFieldImpl(curDesc);
}
return curPut;
}
public void writeFields() throws IOException {
if (curPut == null) {
throw new NotActiveException("no current PutField object");
}
bout.setBlockDataMode(false);
curPut.writeFields();
bout.setBlockDataMode(true);
}
private void writeFatalException(IOException ex) throws IOException {
clear();
boolean oldMode = bout.setBlockDataMode(false);
try {
bout.writeByte(TC_EXCEPTION);
writeObject0(ex, false);
clear();
} finally {
bout.setBlockDataMode(oldMode);
}
}
private void writeNull() throws IOException {
bout.writeByte(TC_NULL);
}
private void writeHandle(int handle) throws IOException {
bout.writeByte(TC_REFERENCE);
bout.writeInt(baseWireHandle + handle);
}
private void writeClass(Class cl, boolean unshared) throws IOException {
bout.writeByte(TC_CLASS);
writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
handles.assign(unshared ? null : cl);
}
private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
bout.writeByte(TC_PROXYCLASSDESC);
handles.assign(unshared ? null : desc);
Class cl = desc.forClass();
Class[] ifaces = cl.getInterfaces();
bout.writeInt(ifaces.length);
for (int i = 0; i < ifaces.length; i++) {
bout.writeUTF(ifaces[i].getName());
}
bout.setBlockDataMode(true);
annotateProxyClass(cl);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
writeClassDesc(desc.getSuperDesc(), false);
}
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
bout.writeByte(TC_CLASSDESC);
handles.assign(unshared ? null : desc);
if (protocol == PROTOCOL_VERSION_1) {
// do not invoke class descriptor write hook with old protocol
desc.writeNonProxy(this);
} else {
writeClassDescriptor(desc);
}
Class cl = desc.forClass();
bout.setBlockDataMode(true);
annotateClass(cl);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
writeClassDesc(desc.getSuperDesc(), false);
}
private void writeString(String str, boolean unshared) throws IOException {
handles.assign(unshared ? null : str);
long utflen = bout.getUTFLength(str);
if (utflen <= 0xFFFF) {
bout.writeByte(TC_STRING);
bout.writeUTF(str, utflen);
} else {
bout.writeByte(TC_LONGSTRING);
bout.writeLongUTF(str, utflen);
}
}
private void writeArray(Object array,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
bout.writeByte(TC_ARRAY);
writeClassDesc(desc, false);
handles.assign(unshared ? null : array);
Class ccl = desc.forClass().getComponentType();
if (ccl.isPrimitive()) {
if (ccl == Integer.TYPE) {
int[] ia = (int[]) array;
bout.writeInt(ia.length);
bout.writeInts(ia, 0, ia.length);
} else if (ccl == Byte.TYPE) {
byte[] ba = (byte[]) array;
bout.writeInt(ba.length);
bout.write(ba, 0, ba.length, true);
} else if (ccl == Long.TYPE) {
long[] ja = (long[]) array;
bout.writeInt(ja.length);
bout.writeLongs(ja, 0, ja.length);
} else if (ccl == Float.TYPE) {
float[] fa = (float[]) array;
bout.writeInt(fa.length);
bout.writeFloats(fa, 0, fa.length);
} else if (ccl == Double.TYPE) {
double[] da = (double[]) array;
bout.writeInt(da.length);
bout.writeDoubles(da, 0, da.length);
} else if (ccl == Short.TYPE) {
short[] sa = (short[]) array;
bout.writeInt(sa.length);
bout.writeShorts(sa, 0, sa.length);
} else if (ccl == Character.TYPE) {
char[] ca = (char[]) array;
bout.writeInt(ca.length);
bout.writeChars(ca, 0, ca.length);
} else if (ccl == Boolean.TYPE) {
boolean[] za = (boolean[]) array;
bout.writeInt(za.length);
bout.writeBooleans(za, 0, za.length);
} else {
throw new InternalError();
}
} else {
Object[] objs = (Object[]) array;
int len = objs.length;
bout.writeInt(len);
if (extendedDebugInfo) {
debugInfoStack.push(
"array (class \"" + array.getClass().getName() +
"\", size: " + len + ")");
}
try {
for (int i = 0; i < len; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"element of array (index: " + i + ")");
}
try {
writeObject0(objs[i], false);
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
private void writeEnum(Enum en,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
bout.writeByte(TC_ENUM);
ObjectStreamClass sdesc = desc.getSuperDesc();
writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
handles.assign(unshared ? null : en);
writeString(en.name(), false);
}
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
if (extendedDebugInfo) {
debugInfoStack.push(
(depth == 1 ? "root " : "") + "object (class \"" +
obj.getClass().getName() + "\", " + obj.toString() + ")");
}
try {
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
private void writeExternalData(Externalizable obj) throws IOException {
PutFieldImpl oldPut = curPut;
curPut = null;
if (extendedDebugInfo) {
debugInfoStack.push("writeExternal data");
}
SerialCallbackContext oldContext = curContext;
try {
curContext = null;
if (protocol == PROTOCOL_VERSION_1) {
obj.writeExternal(this);
} else {
bout.setBlockDataMode(true);
obj.writeExternal(this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
}
} finally {
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
}
private void writeSerialData(Object obj, ObjectStreamClass desc)
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slotDesc.hasWriteObjectMethod()) {
PutFieldImpl oldPut = curPut;
curPut = null;
SerialCallbackContext oldContext = curContext;
if (extendedDebugInfo) {
debugInfoStack.push(
"custom writeObject data (class \"" +
slotDesc.getName() + "\")");
}
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bout.setBlockDataMode(true);
slotDesc.invokeWriteObject(obj, this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
} else {
defaultWriteFields(obj, slotDesc);
}
}
}
public void defaultWriteObject() throws IOException {
if ( curContext == null ) {
throw new NotActiveException("not in call to writeObject");
}
Object curObj = curContext.getObj();
ObjectStreamClass curDesc = curContext.getDesc();
bout.setBlockDataMode(false);
defaultWriteFields(curObj, curDesc);
bout.setBlockDataMode(true);
}
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
throws IOException
{
// REMIND: perform conservative isInstance check here?
desc.checkDefaultSerialize();
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false);
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"field (class \"" + desc.getName() + "\", name: \"" +
fields[numPrimFields + i].getName() + "\", type: \"" +
fields[numPrimFields + i].getType() + "\")");
}
try {
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
protected void writeClassDescriptor(ObjectStreamClass desc)
throws IOException
{
desc.writeNonProxy(this);
}
void writeTypeString(String str) throws IOException {
int handle;
if (str == null) {
writeNull();
} else if ((handle = handles.lookup(str)) != -1) {
writeHandle(handle);
} else {
writeString(str, false);
}
}
private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
int handle;
if (desc == null) {
writeNull();
} else if (!unshared && (handle = handles.lookup(desc)) != -1) {
writeHandle(handle);
} else if (desc.isProxy()) {
writeProxyDesc(desc, unshared);
} else {
writeNonProxyDesc(desc, unshared);
}
}
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
protected void writeObjectOverride(Object obj) throws IOException {
}
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// handle previously written and non-replaceable objects
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
// check for replacement object
Object orig = obj;
Class cl = obj.getClass();
ObjectStreamClass desc;
for (;;) {
// REMIND: skip this check for strings/arrays?
Class repCl;
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
if (enableReplace) {
Object rep = replaceObject(obj);
if (rep != obj && rep != null) {
cl = rep.getClass();
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
}
// if object replaced, run through original checks a second time
if (obj != orig) {
subs.assign(orig, obj);
if (obj == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
}
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}
public void writeUnshared(Object obj) throws IOException {
try {
writeObject0(obj, true);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
protected void annotateClass(Class> cl) throws IOException {
}
protected void annotateProxyClass(Class> cl) throws IOException {
}
protected Object replaceObject(Object obj) throws IOException {
return obj;
}
protected boolean enableReplaceObject(boolean enable)
throws SecurityException
{
if (enable == enableReplace) {
return enable;
}
if (enable) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBSTITUTION_PERMISSION);
}
}
enableReplace = enable;
return !enableReplace;
}
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
protected ObjectOutputStream() throws IOException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
bout = null;
handles = null;
subs = null;
enableOverride = true;
debugInfoStack = null;
}
ObjectOutputStream类的构造方法有两个,一个public的单参数构造函数,一个protected的无参构造函数,看看两个构造函数的详细流程:
private void verifySubclass() {
Class cl = getClass();
if (cl == ObjectOutputStream.class) {
return;
}
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return;
}
processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
Boolean result = Caches.subclassAudits.get(key);
if (result == null) {
result = Boolean.valueOf(auditSubclass(cl));
Caches.subclassAudits.putIfAbsent(key, result);
}
if (result.booleanValue()) {
return;
}
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
private static boolean auditSubclass(final Class subcl) {
Boolean result = AccessController.doPrivileged(
new PrivilegedAction() {
public Boolean run() {
for (Class cl = subcl;
cl != ObjectOutputStream.class;
cl = cl.getSuperclass())
{
try {
cl.getDeclaredMethod(
"writeUnshared", new Class[] { Object.class });
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
try {
cl.getDeclaredMethod("putFields", (Class[]) null);
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.TRUE;
}
}
);
return result.booleanValue();
}
private void clear() {
subs.clear();
handles.clear();
}
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
private static class Caches
public static abstract class GetField
private class GetFieldImpl extends GetField
private static class ValidationList
private static class PeekInputStream extends InputStream
private class BlockDataInputStream extends InputStream implements DataInput
private static class HandleTable
/** end offset of valid data in buf, or -1 if no more block data */
private int end = -1;
/** number of bytes in current block yet to be read from stream */
private int unread = 0;
int currentBlockRemaining() {
if (blkmode) {
return (end >= 0) ? (end - pos) + unread : 0;
} else {
throw new IllegalStateException();
}
}
void skipBlockData() throws IOException {
if (!blkmode) {
throw new IllegalStateException("not in block data mode");
}
while (end >= 0) {
refill();
}
}
public long skip(long len) throws IOException {
long remain = len;
while (remain > 0) {
if (blkmode) {
if (pos == end) {
refill();
}
if (end < 0) {
break;
}
int nread = (int) Math.min(remain, end - pos);
remain -= nread;
pos += nread;
} else {
int nread = (int) Math.min(remain, MAX_BLOCK_SIZE);
if ((nread = in.read(buf, 0, nread)) < 0) {
break;
}
remain -= nread;
}
}
return len - remain;
}
private static final byte STATUS_OK = 1;
private static final byte STATUS_UNKNOWN = 2;
private static final byte STATUS_EXCEPTION = 3;
void markDependency(int dependent, int target) {
if (dependent == NULL_HANDLE || target == NULL_HANDLE) {
return;
}
switch (status[dependent]) {
case STATUS_UNKNOWN:
switch (status[target]) {
case STATUS_OK:
// ignore dependencies on objs with no exception
break;
case STATUS_EXCEPTION:
// eagerly propagate exception
markException(dependent,
(ClassNotFoundException) entries[target]);
break;
case STATUS_UNKNOWN:
// add to dependency list of target
if (deps[target] == null) {
deps[target] = new HandleList();
}
deps[target].add(dependent);
// remember lowest unresolved target seen
if (lowDep < 0 || lowDep > target) {
lowDep = target;
}
break;
default:
throw new InternalError();
}
break;
case STATUS_EXCEPTION:
break;
default:
throw new InternalError();
}
}
void markException(int handle, ClassNotFoundException ex) {
switch (status[handle]) {
case STATUS_UNKNOWN:
status[handle] = STATUS_EXCEPTION;
entries[handle] = ex;
// propagate exception to dependents
HandleList dlist = deps[handle];
if (dlist != null) {
int ndeps = dlist.size();
for (int i = 0; i < ndeps; i++) {
markException(dlist.get(i), ex);
}
deps[handle] = null;
}
break;
case STATUS_EXCEPTION:
break;
default:
throw new InternalError();
}
}
private static class PeekInputStream extends InputStream {
/** underlying stream */
private final InputStream in;
/** peeked byte */
private int peekb = -1;
/**
* Creates new PeekInputStream on top of given underlying stream.
*/
PeekInputStream(InputStream in) {
this.in = in;
}
/**
* Peeks at next byte value in stream. Similar to read(), except
* that it does not consume the read value.
*/
int peek() throws IOException {
return (peekb >= 0) ? peekb : (peekb = in.read());
}
public int read() throws IOException {
if (peekb >= 0) {
int v = peekb;
peekb = -1;
return v;
} else {
return in.read();
}
}
public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
} else if (peekb < 0) {
return in.read(b, off, len);
} else {
b[off++] = (byte) peekb;
len--;
peekb = -1;
int n = in.read(b, off, len);
return (n >= 0) ? (n + 1) : 1;
}
}
void readFully(byte[] b, int off, int len) throws IOException {
int n = 0;
while (n < len) {
int count = read(b, off + n, len - n);
if (count < 0) {
throw new EOFException();
}
n += count;
}
}
public long skip(long n) throws IOException {
if (n <= 0) {
return 0;
}
int skipped = 0;
if (peekb >= 0) {
peekb = -1;
skipped++;
n--;
}
return skipped + skip(n);
}
public int available() throws IOException {
return in.available() + ((peekb >= 0) ? 1 : 0);
}
public void close() throws IOException {
in.close();
}
}
private static class ValidationList {
private static class Callback {
final ObjectInputValidation obj;
final int priority;
Callback next;
final AccessControlContext acc;
Callback(ObjectInputValidation obj, int priority, Callback next,
AccessControlContext acc)
{
this.obj = obj;
this.priority = priority;
this.next = next;
this.acc = acc;
}
}
private Callback list;
ValidationList() {
}
void register(ObjectInputValidation obj, int priority)
throws InvalidObjectException
{
if (obj == null) {
throw new InvalidObjectException("null callback");
}
Callback prev = null, cur = list;
while (cur != null && priority < cur.priority) {
prev = cur;
cur = cur.next;
}
AccessControlContext acc = AccessController.getContext();
if (prev != null) {
prev.next = new Callback(obj, priority, cur, acc);
} else {
list = new Callback(obj, priority, list, acc);
}
}
void doCallbacks() throws InvalidObjectException {
try {
while (list != null) {
AccessController.doPrivileged(
new PrivilegedExceptionAction()
{
public Void run() throws InvalidObjectException {
list.obj.validateObject();
return null;
}
}, list.acc);
list = list.next;
}
} catch (PrivilegedActionException ex) {
list = null;
throw (InvalidObjectException) ex.getException();
}
}
public void clear() {
list = null;
}
}
/** handle value representing null */
private static final int NULL_HANDLE = -1;
/** marker for unshared objects in internal handle table */
private static final Object unsharedMarker = new Object();
/** table mapping primitive type names to corresponding class objects */
private static final HashMap> primClasses
= new HashMap<>(8, 1.0F);
static {
primClasses.put("boolean", boolean.class);
primClasses.put("byte", byte.class);
primClasses.put("char", char.class);
primClasses.put("short", short.class);
primClasses.put("int", int.class);
primClasses.put("long", long.class);
primClasses.put("float", float.class);
primClasses.put("double", double.class);
primClasses.put("void", void.class);
}
/** filter stream for handling block data conversion */
private final BlockDataInputStream bin;
/** validation callback list */
private final ValidationList vlist;
/** recursion depth */
private int depth;
/** whether stream is closed */
private boolean closed;
/** wire handle -> obj/exception map */
private final HandleTable handles;
/** scratch field for passing handle values up/down call stack */
private int passHandle = NULL_HANDLE;
/** flag set when at end of field value block with no TC_ENDBLOCKDATA */
private boolean defaultDataEnd = false;
/** buffer for reading primitive field values */
private byte[] primVals;
/** if true, invoke readObjectOverride() instead of readObject() */
private final boolean enableOverride;
/** if true, invoke resolveObject() */
private boolean enableResolve;
/**
* Context during upcalls to class-defined readObject methods; holds
* object currently being deserialized and descriptor for current class.
* Null when not during readObject upcall.
*/
private SerialCallbackContext curContext;
/**
* Converts specified span of bytes into float values.
*/
// REMIND: remove once hotspot inlines Float.intBitsToFloat
private static native void bytesToFloats(byte[] src, int srcpos,
float[] dst, int dstpos,
int nfloats);
/**
* Converts specified span of bytes into double values.
*/
// REMIND: remove once hotspot inlines Double.longBitsToDouble
private static native void bytesToDoubles(byte[] src, int srcpos,
double[] dst, int dstpos,
int ndoubles);
/**
* Returns the first non-null class loader (not counting class loaders of
* generated reflection implementation classes) up the execution stack, or
* null if only code from the null class loader is on the stack. This
* method is also called via reflection by the following RMI-IIOP class:
*
* com.sun.corba.se.internal.util.JDKClassLoader
*
* This method should not be removed or its signature changed without
* corresponding modifications to the above class.
*/
// REMIND: change name to something more accurate?
private static native ClassLoader latestUserDefinedLoader();
public boolean readBoolean() throws IOException {
return bin.readBoolean();
}
public byte readByte() throws IOException {
return bin.readByte();
}
public int readUnsignedByte() throws IOException {
return bin.readUnsignedByte();
}
public char readChar() throws IOException {
return bin.readChar();
}
public short readShort() throws IOException {
return bin.readShort();
}
public int readUnsignedShort() throws IOException {
return bin.readUnsignedShort();
}
public int readInt() throws IOException {
return bin.readInt();
}
public long readLong() throws IOException {
return bin.readLong();
}
public float readFloat() throws IOException {
return bin.readFloat();
}
public double readDouble() throws IOException {
return bin.readDouble();
}
public void readFully(byte[] buf) throws IOException {
bin.readFully(buf, 0, buf.length, false);
}
public void readFully(byte[] buf, int off, int len) throws IOException {
int endoff = off + len;
if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
throw new IndexOutOfBoundsException();
}
bin.readFully(buf, off, len, false);
}
public int skipBytes(int len) throws IOException {
return bin.skipBytes(len);
}
@Deprecated
public String readLine() throws IOException {
return bin.readLine();
}
public String readUTF() throws IOException {
return bin.readUTF();
}
public int available() throws IOException {
return bin.available();
}
public void close() throws IOException {
/*
* Even if stream already closed, propagate redundant close to
* underlying stream to stay consistent with previous implementations.
*/
closed = true;
if (depth == 0) {
clear();
}
bin.close();
}
public ObjectInputStream.GetField readFields()
throws IOException, ClassNotFoundException
{
if (curContext == null) {
throw new NotActiveException("not in call to readObject");
}
Object curObj = curContext.getObj();
ObjectStreamClass curDesc = curContext.getDesc();
bin.setBlockDataMode(false);
GetFieldImpl getField = new GetFieldImpl(curDesc);
getField.readFields();
bin.setBlockDataMode(true);
if (!curDesc.hasWriteObjectData()) {
/*
* Fix for 4360508: since stream does not contain terminating
* TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
* knows to simulate end-of-custom-data behavior.
*/
defaultDataEnd = true;
}
return getField;
}
private IOException readFatalException() throws IOException {
if (bin.readByte() != TC_EXCEPTION) {
throw new InternalError();
}
clear();
return (IOException) readObject0(false);
}
private Object readNull() throws IOException {
if (bin.readByte() != TC_NULL) {
throw new InternalError();
}
passHandle = NULL_HANDLE;
return null;
}
private Object readHandle(boolean unshared) throws IOException {
if (bin.readByte() != TC_REFERENCE) {
throw new InternalError();
}
passHandle = bin.readInt() - baseWireHandle;
if (passHandle < 0 || passHandle >= handles.size()) {
throw new StreamCorruptedException(
String.format("invalid handle value: %08X", passHandle +
baseWireHandle));
}
if (unshared) {
// REMIND: what type of exception to throw here?
throw new InvalidObjectException(
"cannot read back reference as unshared");
}
Object obj = handles.lookupObject(passHandle);
if (obj == unsharedMarker) {
// REMIND: what type of exception to throw here?
throw new InvalidObjectException(
"cannot read back reference to unshared object");
}
return obj;
}
private Class readClass(boolean unshared) throws IOException {
if (bin.readByte() != TC_CLASS) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
Class cl = desc.forClass();
passHandle = handles.assign(unshared ? unsharedMarker : cl);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
handles.finish(passHandle);
return cl;
}
private ObjectStreamClass readProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_PROXYCLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);
passHandle = NULL_HANDLE;
int numIfaces = bin.readInt();
String[] ifaces = new String[numIfaces];
for (int i = 0; i < numIfaces; i++) {
ifaces[i] = bin.readUTF();
}
Class cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
try {
if ((cl = resolveProxyClass(ifaces)) == null) {
resolveEx = new ClassNotFoundException("null class");
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
skipCustomData();
desc.initProxy(cl, resolveEx, readClassDesc(false));
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
private ObjectStreamClass readNonProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_CLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);
passHandle = NULL_HANDLE;
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
throw (IOException) new InvalidClassException(
"failed to read class descriptor").initCause(ex);
}
Class cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
try {
if ((cl = resolveClass(readDesc)) == null) {
resolveEx = new ClassNotFoundException("null class");
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
skipCustomData();
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
private String readString(boolean unshared) throws IOException {
String str;
byte tc = bin.readByte();
switch (tc) {
case TC_STRING:
str = bin.readUTF();
break;
case TC_LONGSTRING:
str = bin.readLongUTF();
break;
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
passHandle = handles.assign(unshared ? unsharedMarker : str);
handles.finish(passHandle);
return str;
}
private Object readArray(boolean unshared) throws IOException {
if (bin.readByte() != TC_ARRAY) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
int len = bin.readInt();
Object array = null;
Class cl, ccl = null;
if ((cl = desc.forClass()) != null) {
ccl = cl.getComponentType();
array = Array.newInstance(ccl, len);
}
int arrayHandle = handles.assign(unshared ? unsharedMarker : array);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(arrayHandle, resolveEx);
}
if (ccl == null) {
for (int i = 0; i < len; i++) {
readObject0(false);
}
} else if (ccl.isPrimitive()) {
if (ccl == Integer.TYPE) {
bin.readInts((int[]) array, 0, len);
} else if (ccl == Byte.TYPE) {
bin.readFully((byte[]) array, 0, len, true);
} else if (ccl == Long.TYPE) {
bin.readLongs((long[]) array, 0, len);
} else if (ccl == Float.TYPE) {
bin.readFloats((float[]) array, 0, len);
} else if (ccl == Double.TYPE) {
bin.readDoubles((double[]) array, 0, len);
} else if (ccl == Short.TYPE) {
bin.readShorts((short[]) array, 0, len);
} else if (ccl == Character.TYPE) {
bin.readChars((char[]) array, 0, len);
} else if (ccl == Boolean.TYPE) {
bin.readBooleans((boolean[]) array, 0, len);
} else {
throw new InternalError();
}
} else {
Object[] oa = (Object[]) array;
for (int i = 0; i < len; i++) {
oa[i] = readObject0(false);
handles.markDependency(arrayHandle, passHandle);
}
}
handles.finish(arrayHandle);
passHandle = arrayHandle;
return array;
}
private Enum readEnum(boolean unshared) throws IOException {
if (bin.readByte() != TC_ENUM) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
if (!desc.isEnum()) {
throw new InvalidClassException("non-enum class: " + desc);
}
int enumHandle = handles.assign(unshared ? unsharedMarker : null);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(enumHandle, resolveEx);
}
String name = readString(false);
Enum en = null;
Class cl = desc.forClass();
if (cl != null) {
try {
en = Enum.valueOf(cl, name);
} catch (IllegalArgumentException ex) {
throw (IOException) new InvalidObjectException(
"enum constant " + name + " does not exist in " +
cl).initCause(ex);
}
if (!unshared) {
handles.setObject(enumHandle, en);
}
}
handles.finish(enumHandle);
passHandle = enumHandle;
return en;
}
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
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);
}
passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);
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;
}
private void readExternalData(Externalizable obj, ObjectStreamClass desc)
throws IOException
{
SerialCallbackContext oldContext = curContext;
curContext = null;
try {
boolean blocked = desc.hasBlockExternalData();
if (blocked) {
bin.setBlockDataMode(true);
}
if (obj != null) {
try {
obj.readExternal(this);
} catch (ClassNotFoundException ex) {
/*
* In most cases, the handle table has already propagated
* a CNFException to passHandle at this point; this mark
* call is included to address cases where the readExternal
* method has cons'ed and thrown a new CNFException of its
* own.
*/
handles.markException(passHandle, ex);
}
}
if (blocked) {
skipCustomData();
}
} finally {
curContext = oldContext;
}
/*
* At this point, if the externalizable data was not written in
* block-data form and either the externalizable class doesn't exist
* locally (i.e., obj == null) or readExternal() just threw a
* CNFException, then the stream is probably in an inconsistent state,
* since some (or all) of the externalizable data may not have been
* consumed. Since there's no "correct" action to take in this case,
* we mimic the behavior of past serialization implementations and
* blindly hope that the stream is in sync; if it isn't and additional
* externalizable data remains in the stream, a subsequent read will
* most likely throw a StreamCorruptedException.
*/
}
private void readSerialData(Object obj, ObjectStreamClass desc)
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slots[i].hasData) {
if (obj != null &&
slotDesc.hasReadObjectMethod() &&
handles.lookupException(passHandle) == null)
{
SerialCallbackContext oldContext = curContext;
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bin.setBlockDataMode(true);
slotDesc.invokeReadObject(obj, this);
} catch (ClassNotFoundException ex) {
/*
* In most cases, the handle table has already
* propagated a CNFException to passHandle at this
* point; this mark call is included to address cases
* where the custom readObject method has cons'ed and
* thrown a new CNFException of its own.
*/
handles.markException(passHandle, ex);
} finally {
curContext.setUsed();
curContext = oldContext;
}
/*
* defaultDataEnd may have been set indirectly by custom
* readObject() method when calling defaultReadObject() or
* readFields(); clear it to restore normal read behavior.
*/
defaultDataEnd = false;
} else {
defaultReadFields(obj, slotDesc);
}
if (slotDesc.hasWriteObjectData()) {
skipCustomData();
} else {
bin.setBlockDataMode(false);
}
} else {
if (obj != null &&
slotDesc.hasReadObjectNoDataMethod() &&
handles.lookupException(passHandle) == null)
{
slotDesc.invokeReadObjectNoData(obj);
}
}
}
}
public void defaultReadObject()
throws IOException, ClassNotFoundException
{
if (curContext == null) {
throw new NotActiveException("not in call to readObject");
}
Object curObj = curContext.getObj();
ObjectStreamClass curDesc = curContext.getDesc();
bin.setBlockDataMode(false);
defaultReadFields(curObj, curDesc);
bin.setBlockDataMode(true);
if (!curDesc.hasWriteObjectData()) {
/*
* Fix for 4360508: since stream does not contain terminating
* TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
* knows to simulate end-of-custom-data behavior.
*/
defaultDataEnd = true;
}
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
}
private void defaultReadFields(Object obj, ObjectStreamClass desc)
throws IOException
{
// REMIND: is isInstance check necessary?
Class cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
bin.readFully(primVals, 0, primDataSize, false);
if (obj != null) {
desc.setPrimFieldValues(obj, primVals);
}
int objHandle = passHandle;
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
ObjectStreamField f = fields[numPrimFields + i];
objVals[i] = readObject0(f.isUnshared());
if (f.getField() != null) {
handles.markDependency(objHandle, passHandle);
}
}
if (obj != null) {
desc.setObjFieldValues(obj, objVals);
}
passHandle = objHandle;
}
protected ObjectStreamClass readClassDescriptor()
throws IOException, ClassNotFoundException
{
ObjectStreamClass desc = new ObjectStreamClass();
desc.readNonProxy(this);
return desc;
}
String readTypeString() throws IOException {
int oldHandle = passHandle;
try {
byte tc = bin.peekByte();
switch (tc) {
case TC_NULL:
return (String) readNull();
case TC_REFERENCE:
return (String) readHandle(false);
case TC_STRING:
case TC_LONGSTRING:
return readString(false);
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
passHandle = oldHandle;
}
}
private ObjectStreamClass readClassDesc(boolean unshared)
throws IOException
{
byte tc = bin.peekByte();
switch (tc) {
case TC_NULL:
return (ObjectStreamClass) readNull();
case TC_REFERENCE:
return (ObjectStreamClass) readHandle(unshared);
case TC_PROXYCLASSDESC:
return readProxyDesc(unshared);
case TC_CLASSDESC:
return readNonProxyDesc(unshared);
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
}
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
protected Object readObjectOverride()
throws IOException, ClassNotFoundException
{
return null;
}
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
depth++;
try {
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
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");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
protected void readStreamHeader()
throws IOException, StreamCorruptedException
{
short s0 = bin.readShort();
short s1 = bin.readShort();
if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
throw new StreamCorruptedException(
String.format("invalid stream header: %04X%04X", s0, s1));
}
}
public Object readUnshared() throws IOException, ClassNotFoundException {
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(true);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
protected Class> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String name = desc.getName();
try {
return Class.forName(name, false, latestUserDefinedLoader());
} catch (ClassNotFoundException ex) {
Class> cl = primClasses.get(name);
if (cl != null) {
return cl;
} else {
throw ex;
}
}
}
protected Class> resolveProxyClass(String[] interfaces)
throws IOException, ClassNotFoundException
{
ClassLoader latestLoader = latestUserDefinedLoader();
ClassLoader nonPublicLoader = null;
boolean hasNonPublicInterface = false;
// define proxy in class loader of non-public interface(s), if any
Class[] classObjs = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
Class cl = Class.forName(interfaces[i], false, latestLoader);
if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
if (hasNonPublicInterface) {
if (nonPublicLoader != cl.getClassLoader()) {
throw new IllegalAccessError(
"conflicting non-public interface class loaders");
}
} else {
nonPublicLoader = cl.getClassLoader();
hasNonPublicInterface = true;
}
}
classObjs[i] = cl;
}
try {
return Proxy.getProxyClass(
hasNonPublicInterface ? nonPublicLoader : latestLoader,
classObjs);
} catch (IllegalArgumentException e) {
throw new ClassNotFoundException(null, e);
}
}
protected Object resolveObject(Object obj) throws IOException {
return obj;
}
protected boolean enableResolveObject(boolean enable)
throws SecurityException
{
if (enable == enableResolve) {
return enable;
}
if (enable) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBSTITUTION_PERMISSION);
}
}
enableResolve = enable;
return !enableResolve;
}
public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in);
handles = new HandleTable(10);
vlist = new ValidationList();
enableOverride = false;
readStreamHeader();
bin.setBlockDataMode(true);
}
protected ObjectInputStream() throws IOException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
bin = null;
handles = null;
vlist = null;
enableOverride = true;
}
ObjectInputStream和ObjectOutputStream类一样有两个构造方法,一个public的单参数构造函数,一个protected的无参构造函数,看看两个构造函数的详细流程:
package org.susan.java.serial;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ArraySerial implements Serializable{
/**
*
*/
private static final long serialVersionUID = 749500769727730567L;
private String name;
public ArraySerial(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private int age;
// 运行函数
public static void main(String args[]) throws Exception{
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("array.obj"));
// 序列化普通数组
short[] arr = new short[4];
for( int i = 0; i < 4; i++ ){
short ele = (short) (Math.random() * 100);
arr[i] = ele;
}
out.writeObject(arr);
// 序列化对象数组
ArraySerial[] objArr = new ArraySerial[4];
for( int i = 0; i < 4; i++ ){
objArr[i] = new ArraySerial("LangYu"+i,(int) (Math.random() * 60));
}
out.writeObject(objArr);
out.flush();
out.close();
}
}
Class cl = getClass();
if (cl == ObjectOutputStream.class) {
return;
}
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
bout.setBlockDataMode(true);
if (enableOverride) {
writeObjectOverride(obj);
return;
}
writeObject0(obj, false);
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(cl.getName() + "\n"
+ debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
bout.writeByte(TC_ARRAY);
writeClassDesc(desc, false);
bout.writeByte(TC_CLASSDESC);
handles.assign(unshared ? null : desc);
if (protocol == PROTOCOL_VERSION_1) {
// do not invoke class descriptor write hook with old protocol
desc.writeNonProxy(this);
} else {
writeClassDescriptor(desc);
}
desc.writeNonProxy(this);
out.writeUTF(name);
out.writeLong(getSerialVersionUID());
void writeUTF(String s, long utflen) throws IOException {
if (utflen > 0xFFFFL) {
throw new UTFDataFormatException();
}
writeShort((int) utflen);
if (utflen == (long) s.length()) {
writeBytes(s);
} else {
writeUTFBody(s);
}
}
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
out.writeByte(flags);
out.writeShort(fields.length);
Class cl = desc.forClass();
bout.setBlockDataMode(true);
annotateClass(cl);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
writeClassDesc(desc.getSuperDesc(), false);
bout.writeByte(TC_NULL);
Class ccl = desc.forClass().getComponentType();
if (ccl.isPrimitive()) {
// 如果元素是基础类型的代码段
...
} else {
// 如果元素是对象类型的代码段
...
}
short[] sa = (short[]) array;
bout.writeInt(sa.length);
bout.writeShorts(sa, 0, sa.length);
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
Object[] objs = (Object[]) array;
int len = objs.length;
bout.writeInt(len);
for (int i = 0; i < len; i++) {
if (extendedDebugInfo) {
debugInfoStack.push("element of array (index: " + i + ")");
}
try {
writeObject0(objs[i], false);
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
writeOrdinaryObject(obj, desc, unshared);
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
writeClassDesc(desc, false);
bout.writeByte(TC_CLASSDESC);
handles.assign(unshared ? null : desc);
out.writeUTF(name);
out.writeLong(getSerialVersionUID());
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());
}
}
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
defaultWriteFields(obj, slotDesc);
desc.checkDefaultSerialize();
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false);
bout.writeByte(TC_REFERENCE);
bout.writeInt(baseWireHandle + handle);
package org.susan.java.serial;
import java.io.FileInputStream;
import java.io.FileOutputStream;
// import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CaseOne {
// 执行函数
public static void main(String args[]) throws Exception{
// 序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("caseone.obj"));
SubClass sub = new SubClass(true,"silentbalanceyh","Yu Lang");
out.writeObject(sub);
out.flush();
out.close();
// 反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("caseone.obj"));
SubClass subNew = (SubClass)in.readObject();
in.close();
System.out.println("SUB:" + subNew);
System.out.println("SUPER:" + subNew.getSuper());
}
}
class SubClass extends BaseClass implements Serializable{
private static final long serialVersionUID = 7442418880476300463L;
private boolean gender;
private String name;
private String author;
public SubClass(){
super();
}
public SubClass(boolean gender,String name, String author){
super(name,27);
this.gender = gender;
this.name = name;
this.author = author;
}
public String getSuper(){
return super.toString();
}
/* private void writeObject(ObjectOutputStream out) throws IOException{
out.defaultWriteObject();
out.writeObject(super.name);
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{
in.defaultReadObject();
super.name = (String)in.readObject();
age = (int)in.readInt();
}*/
@Override
public String toString() {
return "SubClass [gender=" + gender + ", name=" + name + ", author="
+ author + "]";
}
}
class BaseClass{
@Override
public String toString() {
return "BaseClass [name=" + name + ", age=" + age + "]";
}
protected String name;
protected int age;
public BaseClass(){
}
public BaseClass(String name, int age){
this.name = name;
this.age = age;
}
}
package org.susan.java.serial;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CaseTwo implements Serializable{
private static final long serialVersionUID = -5719199880473656625L;
public CaseTwo(){
System.out.println("Called Constructor!");
}
public static void main(String args[]) throws Exception{
// 序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("casetwo.obj"));
CaseTwo sub = new CaseTwo();
out.writeObject(sub);
out.flush();
out.close();
// 反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("casetwo.obj"));
@SuppressWarnings("unused")
CaseTwo subNew = (CaseTwo)in.readObject();
in.close();
}
}
package org.susan.java.serial;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CaseThree {
public static void main(String args[]) throws Exception {
// 序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
"casethree.obj"));
BaseClass1 base = new BaseClass1();
out.writeObject(base);
out.flush();
out.close();
}
}
class BaseClass1 extends AClass1 implements AInterface,Serializable {
private static final long serialVersionUID = 3530141157381778908L;
}
abstract class AClass1 implements CInterface {
}
interface AInterface extends BInterface {
int name = 0;
}
interface BInterface {
String bName = "Lang Yu";
}
interface CInterface {
}
AInterface base = new BaseClass1();
CInterface base = new BaseClass1();
BInterface base = new BaseClass1();
AClass1 base = new BaseClass1();