summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2012-01-04 08:36:25 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-01-04 08:36:25 -0800
commit3167109e2aa1f9d2b02515bf7a2277ec94e821a2 (patch)
treeaa8efcc7d799cccabb1dd586b4406caf159b17ce
parenta9d79f124eb91eab9f7ec8f5fa6b5f64631533a2 (diff)
parent47f29998eae296ad812d697c8da12e84e394fee2 (diff)
downloadlibcore-3167109e2aa1f9d2b02515bf7a2277ec94e821a2.zip
libcore-3167109e2aa1f9d2b02515bf7a2277ec94e821a2.tar.gz
libcore-3167109e2aa1f9d2b02515bf7a2277ec94e821a2.tar.bz2
Merge "Discourage Serializable."
-rw-r--r--luni/src/main/java/java/io/Serializable.java91
-rw-r--r--luni/src/test/java/libcore/java/io/SerializationTest.java34
2 files changed, 111 insertions, 14 deletions
diff --git a/luni/src/main/java/java/io/Serializable.java b/luni/src/main/java/java/io/Serializable.java
index 9cd7816..f1256bb 100644
--- a/luni/src/main/java/java/io/Serializable.java
+++ b/luni/src/main/java/java/io/Serializable.java
@@ -18,20 +18,83 @@
package java.io;
/**
- * An empty marker interface for classes that want to support serialization and
- * deserialization based on the {@code ObjectOutputStream} and {@code
- * ObjectInputStream} classes. Implementing this interface is enough to make
- * most classes serializable. If a class needs more fine-grained control over
- * the serialization process (for example to implement compatibility with older
- * versions of the class), it can achieve this by providing the following two
- * methods (signatures must match exactly):
- * <p>
- * {@code private void writeObject(java.io.ObjectOutputStream out) throws
- * IOException}
- * <p>
- * {@code private void readObject(java.io.ObjectInputStream in) throws
- * IOException, ClassNotFoundException}
+ * Marks classes that can be serialized by {@link ObjectOutputStream} and
+ * deserialized by {@link ObjectInputStream}.
+ *
+ * <p><strong>Warning:</strong> this interface limits how its implementing
+ * classes can change in the future. By implementing {@code Serializable} you
+ * expose your flexible in-memory implementation details as a rigid binary
+ * representation. Simple code changes--like renaming private fields--are
+ * not safe when the changed class is serializable.
+ *
+ * <h3>The Serialized Form</h3>
+ * By default, the serialization mechanism encodes an object's class name, the
+ * names of its non-transient fields (including non-public fields), and the
+ * values of all of those fields. The output is an opaque sequence of bytes.
+ * Those bytes can be decoded into a new, equivalent instance as long as the
+ * decoder has compatible versions of the originating classes.
+ *
+ * <p>Changing the class name, field names or field types breaks serialization
+ * compatibility and complicates interoperability between old and new versions
+ * of the serializable class. Adding or removing fields also complicates
+ * serialization between versions of a class because it requires your code to
+ * cope with missing fields.
+ *
+ * <p>Every serializable class is assigned a version identifier called a {@code
+ * serialVersionUID}. By default, this identifier is computed by hashing the
+ * class declaration and its members. This identifier is included in the
+ * serialized form so that version conflicts can be detected during
+ * deserialization. If the local {@code serialVersionUID} differs from the
+ * {@code serialVersionUID} in the serialized data, deserialization will fail
+ * with an {@link InvalidClassException}.
+ *
+ * <p>You can avoid this failure by declaring an explicit {@code
+ * serialVersionUID}. Declaring an explicit {@code serialVersionUID} tells the
+ * serialization mechanism that the class is forward and backward compatible
+ * with all versions that share that {@code serialVersionUID}. Declaring a
+ * {@code serialVersionUID} looks like this: <pre> {@code
+ *
+ * private static final long serialVersionUID = 0L;
+ * }</pre>
+ * If you declare a {@code serialVersionUID}, you should increment it each
+ * time your class changes incompatibly with the previous version. Typically
+ * this is when you add, change or remove a non-transient field.
+ *
+ * <p>You can take control of your serialized form by implementing these two
+ * methods with these exact signatures in your serializable classes:
+ * <pre> {@code
+ *
+ * private void writeObject(java.io.ObjectOutputStream out)
+ * throws IOException {
+ * // write 'this' to 'out'...
+ * }
+ *
+ * private void readObject(java.io.ObjectInputStream in)
+ * throws IOException, ClassNotFoundException {
+ * // populate the fields of 'this' from the data in 'in'...
+ * }
+ * }</pre>
+ * It is impossible to maintain serialization compatibility across a class name
+ * change. For this reason, implementing {@code Serializable} in anonymous
+ * inner classes is highly discouraged: simply reordering the members in the
+ * file could change the generated class name and break serialization
+ * compatibility.
+ *
+ * <p>You can exclude member fields from serialization by giving them the {@code
+ * transient} modifier. Upon deserialization, the transient field's value will
+ * be null, 0, or false according to its type.
+ *
+ * <h3>Implement Serializable Judiciously</h3>
+ * Refer to <i>Effective Java</i>'s chapter on serialization for thorough
+ * coverage of the serialization API. The book explains how to use this
+ * interface without harming your application's maintainability.
+ *
+ * <h3>Recommended Alternatives</h3>
+ * <strong>JSON</strong> is concise, human-readable and efficient. Android
+ * includes both a {@link android.util.JsonReader streaming API} and a {@link
+ * org.json.JSONObject tree API} to read and write JSON. Use a binding library
+ * like <a href="http://code.google.com/p/google-gson/">GSON</a> to read and
+ * write Java objects directly.
*/
public interface Serializable {
- /* empty */
}
diff --git a/luni/src/test/java/libcore/java/io/SerializationTest.java b/luni/src/test/java/libcore/java/io/SerializationTest.java
index 85cb307..434dd56 100644
--- a/luni/src/test/java/libcore/java/io/SerializationTest.java
+++ b/luni/src/test/java/libcore/java/io/SerializationTest.java
@@ -16,6 +16,8 @@
package libcore.java.io;
+import java.io.IOException;
+import java.io.InvalidClassException;
import java.io.Serializable;
import junit.framework.TestCase;
import libcore.util.SerializationTester;
@@ -36,4 +38,36 @@ public final class SerializationTest extends TestCase {
private static final long serialVersionUID = 0L;
private transient int transientInt;
}
+
+ public void testSerialVersionUidChange() throws Exception {
+ // this was created by serializing a SerialVersionUidChanged with serialVersionUID = 0L
+ String s = "aced0005737200396c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ + "3742453657269616c56657273696f6e5569644368616e67656400000000000000000200014900016"
+ + "1787000000003";
+ try {
+ SerializationTester.deserializeHex(s);
+ fail();
+ } catch (InvalidClassException expected) {
+ }
+ }
+
+ static class SerialVersionUidChanged implements Serializable {
+ private static final long serialVersionUID = 1L; // was 0L
+ private int a;
+ }
+
+ public void testMissingSerialVersionUid() throws Exception {
+ // this was created by serializing a FieldsChanged with one int field named 'a'
+ String s = "aced00057372002f6c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ + "374244669656c64734368616e6765643bcfb934e310fa1c02000149000161787000000003";
+ try {
+ SerializationTester.deserializeHex(s);
+ fail();
+ } catch (InvalidClassException expected) {
+ }
+ }
+
+ static class FieldsChanged implements Serializable {
+ private int b; // was 'a'
+ }
}