diff options
author | Jesse Wilson <jessewilson@google.com> | 2012-01-04 08:36:25 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-01-04 08:36:25 -0800 |
commit | 3167109e2aa1f9d2b02515bf7a2277ec94e821a2 (patch) | |
tree | aa8efcc7d799cccabb1dd586b4406caf159b17ce | |
parent | a9d79f124eb91eab9f7ec8f5fa6b5f64631533a2 (diff) | |
parent | 47f29998eae296ad812d697c8da12e84e394fee2 (diff) | |
download | libcore-3167109e2aa1f9d2b02515bf7a2277ec94e821a2.zip libcore-3167109e2aa1f9d2b02515bf7a2277ec94e821a2.tar.gz libcore-3167109e2aa1f9d2b02515bf7a2277ec94e821a2.tar.bz2 |
Merge "Discourage Serializable."
-rw-r--r-- | luni/src/main/java/java/io/Serializable.java | 91 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/io/SerializationTest.java | 34 |
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' + } } |