title: java customize ObjectOutputStream to write additional data for each instance of a specific class
body: <p>There're some library classes that although implement <code>Serializable</code>, fail to serialize correctly. I can't fix them, but I can extend <code>ObjectOutputStream</code> and <code>ObjectInputStream</code> for some workaround.</p>
  
  <p>I want my <code>ObjectOutputStream</code> to write additional data for each instance of class <code>A</code> and <code>ObjectInputStream</code> to read and apply that data after <code>A</code> is deserialized.</p>
  
  <p>Currently I have a mid-workaround that requires explicit additional calls to <code>writeObject()</code> and <code>readObject()</code>. I'd prefer to manage without these calls.</p>
  
  <p>Uncomment /* */ blocks to see how it works.</p>
  
  <pre><code>import java.io.*;
  import java.lang.reflect.Field;
  import java.util.*;
  import org.junit.Test;
  import static org.junit.Assert.*;
  public class CloneSerializableTest2 {
      // library classes
          public static class A implements Serializable {
              public transient String s1;
          }
  
          public static class MyA extends A {
  
              public String s2;
          }
  
  /*
      private static class AHolder implements Serializable {
          private static final Field s1Fld;
          static {
              try {
                  s1Fld = A.class.getDeclaredField("s1");
                  s1Fld.setAccessible(true);
              } catch (NoSuchFieldException e) {
                  throw new RuntimeException("Unexpected error", e);
              }
          }
          private String s1;
          private A a;
          public AHolder(A m) {
              this.a = m;
              try {
                  s1 = (String)s1Fld.get(m);
              } catch (IllegalAccessException e) {
                  throw new RuntimeException("Unexpected error", e);
              }
          }
          public void restoreA() {
              try {
                  s1Fld.set(a, s1);
              } catch (IllegalAccessException e) {
                  throw new RuntimeException("Unexpected error", e);
              }
          }
      }
  */
      @SuppressWarnings("unchecked")
      public static &lt;T&gt; T cloneSerializable(T o) {
          try {
  /*
              final List&lt;AHolder&gt; accumSrc = new ArrayList&lt;AHolder&gt;();
  */
              ByteArrayOutputStream bout = new ByteArrayOutputStream();
              ObjectOutputStream out = new ObjectOutputStream(bout)
  /*
              {
                  {
                      enableReplaceObject(true);
                  }
                  @Override
                  protected Object replaceObject(Object obj) throws IOException
                  {
                      if (obj instanceof A) {
                          accumSrc.add(new AHolder((A)obj));
                      }
                      return super.replaceObject(obj);
                  }
              }
  */
              ;
              out.writeObject(o);
  /*
              out.writeObject(accumSrc);
  */
              out.close();
              ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
              ObjectInputStream in = new ObjectInputStream(bin);
              Object copy = in.readObject();
  /*
              List&lt;AHolder&gt; accumDst = (List&lt;AHolder&gt;)in.readObject();
              for (AHolder r : accumDst) {
                  r.restoreA();
              }
  */
              in.close();
              return (T)copy;
          } catch (IOException e) {
              throw new RuntimeException("Unexpected error", e);
          } catch (ClassNotFoundException e) {
              throw new RuntimeException("Unexpected error", e);
          }
      }
      @Test
      public void testIt() throws Exception {
          try {
          MyA m1 = new MyA();
          m1.s1 = "a";
          m1.s2 = "b";
  
          m1 = cloneSerializable(m1);
          assertEquals("a", m1.s1);
          assertEquals("b", m1.s2);
          } catch (Exception e) {
              e.printStackTrace();
              throw e;
          }
      }
  }
  </code></pre>