writeObject和readObject方法
  在ArrayList中定义了来个方法: writeObject和readObject。
  这里先给出结论:
  在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。
  如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。
  用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。
  来看一下这两个方法的具体实现:
  code 5
  private void readObject(java.io.ObjectInputStream s)
  throws java.io.IOException, ClassNotFoundException {
  elementData = EMPTY_ELEMENTDATA;
  // Read in size, and any hidden stuff
  s.defaultReadObject();
  // Read in capacity
  s.readInt(); // ignored
  if (size > 0) {
  // be like clone(), allocate array based upon size not capacity
  ensureCapacityInternal(size);
  Object[] a = elementData;
  // Read in all elements in the proper order.
  for (int i=0; i<size; i++) {
  a[i] = s.readObject();
  }
  }
  }
  code 6
  private void writeObject(java.io.ObjectOutputStream s)
  throws java.io.IOException{
  // Write out element count, and any hidden stuff
  int expectedModCount = modCount;
  s.defaultWriteObject();
  // Write out size as capacity for behavioural compatibility with clone()
  s.writeInt(size);
  // Write out all elements in the proper order.
  for (int i=0; i<size; i++) {
  s.writeObject(elementData[i]);
  }
  if (modCount != expectedModCount) {
  throw new ConcurrentModificationException();
  }
  }
  那么为什么ArrayList要用这种方式来实现序列化呢?
  why transient
  ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那会序列化99个null元素。为了保证在序列化的时候不会将这么多null同时进行序列化,ArrayList把元素数组设置为transient。
  why writeObject and readObject
  前面说过,为了防止一个包含大量空对象的数组被序列化,为了优化存储,所以,ArrayList使用transient来声明elementData。 但是,作为一个集合,在序列化过程中还必须保证其中的元素可以被持久化下来,所以,通过重写writeObject 和 readObject方法的方式把其中的元素保留下来。
  writeObject方法把elementData数组中的元素遍历的保存到输出流(ObjectOutputStream)中。
  readObject方法从输入流(ObjectInputStream)中读出对象并保存赋值到elementData数组中。
  至此,我们先试着来回答刚刚提出的问题:
  如何自定义的序列化和反序列化策略
  答:可以通过在被序列化的类中增加writeObject 和 readObject方法。那么问题又来了:
  虽然ArrayList中写了writeObject 和 readObject 方法,但是这两个方法并没有显示的被调用啊。
  那么如果一个类中包含writeObject 和 readObject 方法,那么这两个方法是怎么被调用的呢?
  ObjectOutputStream
  从code 4中,我们可以看出,对象的序列化过程通过ObjectOutputStream和ObjectInputputStream来实现的,那么带着刚刚的问题,我们来分析一下ArrayList中的writeObject 和 readObject 方法到底是如何被调用的呢?
  为了节省篇幅,这里给出ObjectOutputStream的writeObject的调用栈:
  writeObject ---> writeObject0 --->writeOrdinaryObject--->writeSerialData--->invokeWriteObject
  这里看一下invokeWriteObject:
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(ex);
}
} else {
throw new UnsupportedOperationException();
}
}
  其中writeObjectMethod.invoke(obj, new Object[]{ out });是关键,通过反射的方式调用writeObjectMethod方法。官方是这么解释这个writeObjectMethod的:
  class-defined writeObject method, or null if none
  在我们的例子中,这个方法是我们在ArrayList中定义的writeObject方法。通过反射的方式被调用了。
  至此,我们先试着来回答刚刚提出的问题:
  如果一个类中包含writeObject 和 readObject 方法,那么这两个方法是怎么被调用的?
  答:在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法时,会通过反射的方式调用。
  至此,我们已经介绍完了ArrayList的序列化方式。那么,不知道有没有人提出这样的疑问:
  Serializable明明是一个空的接口,它是怎么保证只有实现了该接口的方法才能进行序列化与反序列化的呢?
  Serializable接口的定义:
  public interface Serializable {
  }
  读者可以尝试把code 1中的继承Serializable的代码去掉,再执行code 2,会抛出java.io.NotSerializableException。
  其实这个问题也很好回答,我们再回到刚刚ObjectOutputStream的writeObject的调用栈:
  writeObject ---> writeObject0 --->writeOrdinaryObject--->writeSerialData--->invokeWriteObject
  writeObject0方法中有这么一段代码:
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() + " " + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
  在进行序列化操作时,会判断要被序列化的类是否是Enum、Array和Serializable类型,如果不是则直接抛出NotSerializableException。
  总结
  1、如果一个类想被序列化,需要实现Serializable接口。否则将抛出NotSerializableException异常,这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。
  2、在变量声明前加上该关键字,可以阻止该变量被序列化到文件中。
  3、在类中增加writeObject 和 readObject 方法可以实现自定义序列化策略