前言:在寫 分析輪子(一)-ArrayList.java 的時候曾經下過一個結論 「實現Serializable接口,表示ArrayList是可序列化的」,這個結論是以往學習的經驗所得,而且平時在編程的時候也遇到過其餘的問題,好比:在寫 IDEA使用筆記(八)——自動生成 serialVersionUID 的設置 的時候,其實就遇到了一個對象序列化和反序列化相關的問題,後來解決了,不過沒有深刻下去和總結一下。編程這件事情,最好實驗一把,就算是他人已經研究明白的東西,本身若是不動手試試,可能印象總不是特別的深入,哪怕隨便玩一下,也許都會有徹底不一樣的收穫。html
注:玩的是JDK1.7版 紙上得來終覺淺,絕知此事要躬行。(自勉之。。。)java
一:若是不實現 Serializable.java 接口,我要序列化對象會怎樣呢?編程
1)來個簡單的Bean,故意不實現 Serializable.java接口,以下所示oracle
/** * @description:人類 * @author:godtrue * @create:2018-09-09 */ public class Person {//注意這裏哈! /** * 身份證號 */ private int id; /** * 姓名 */ private String name; /** * 性別 */ private boolean sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Person{"); sb.append("id=").append(id); sb.append(", name='").append(name).append('\''); sb.append(", sex=").append(sex); sb.append('}'); return sb.toString(); } }
2)來個序列化和反序列化的測試類,跑一下,看看狀況如何?app
/** * @description:序列化和反序列化測試類 * @author:godtrue * @create:2018-09-09 */ public class SerializeAndDeserialize{ /** * *@description: 測試入口,主方法 *@param args *@return: void *@author: godtrue *@createTime: 2018-09-09 *@version: v1.0 */ public static void main(String[] args){ Person person = new Person(); person.setId(1111); person.setName("雙十一"); person.setSex(true); try { serializePerson(person); Person personTemp = deserializePerson(); System.out.println(personTemp); }catch (Exception e){ e.printStackTrace(); } } /** * *@description: 序列化人類對象方法 *@param person *@return: void *@author: godtrue *@createTime: 2018-09-09 *@version: v1.0 */ private static void serializePerson(Person person) throws IOException{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://PersonInfo.text")); objectOutputStream.writeObject(person); System.out.println("serialize person success"); objectOutputStream.close(); } /** * *@description: 反序列化人類方法 *@param *@return: com.godtrue.Person *@author: gotrue *@createTime: 2018-09-09 *@version: v1.0 */ private static Person deserializePerson() throws ClassNotFoundException,IOException{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://PersonInfo.text")); Person person = (Person) objectInputStream.readObject(); System.out.println("deserialize person success"); return person; } }
3)不實現 Serializable.java 接口,進行對象序列化的後果很嚴重(序列化不成),而且拋出 java.io.NotSerializableException 異常socket
Connected to the target VM, address: '127.0.0.1:53429', transport: 'socket' java.io.NotSerializableException: com.godtrue.Person at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347) at com.godtrue.SerializeAndDeserialize.serializePerson(SerializeAndDeserialize.java:28) at com.godtrue.SerializeAndDeserialize.main(SerializeAndDeserialize.java:18) Disconnected from the target VM, address: '127.0.0.1:53429', transport: 'socket' Process finished with exit code 0
4)跟一下,看看這個異常時哪裏拋出來的ide
/** * Write the specified object to the ObjectOutputStream. The class of the * object, the signature of the class, and the values of the non-transient * and non-static fields of the class and all of its supertypes are * written. Default serialization for a class can be overridden using the * writeObject and the readObject methods. Objects referenced by this * object are written transitively so that a complete equivalent graph of * objects can be reconstructed by an ObjectInputStream. * * <p>Exceptions are thrown for problems with the OutputStream and for * classes that should not be serialized. All exceptions are fatal to the * OutputStream, which is left in an indeterminate state, and it is up to * the caller to ignore or recover the stream state. * * @throws InvalidClassException Something is wrong with a class used by * serialization. * @throws NotSerializableException Some object to be serialized does not * implement the java.io.Serializable interface. * @throws IOException Any exception thrown by the underlying * OutputStream. */ 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; } }
/** * Underlying writeObject/writeUnshared implementation. */ 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); } }
二:嗯,那好吧!如今終於明白若是不實現 Serializable.java 接口,序列化對象的後果了,那就實現一下吧!畢竟JAVA平臺都這麼規定了,除非換個平臺了!工具
1)調整 Person.java 類,使其實現 Serializable.java 接口,以下所示post
/** * @description:人類 * @author:godtrue * @create:2018-09-09 */ public class Person implements Serializable{//注意這裏啦! /** * 身份證號 */ private int id; /** * 姓名 */ private String name; /** * 性別 */ private boolean sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Person{"); sb.append("id=").append(id); sb.append(", name='").append(name).append('\''); sb.append(", sex=").append(sex); sb.append('}'); return sb.toString(); } }
2)序列化和反序列化的測試類 原封未動,這裏就不貼出來了學習
3)Person.java 實現 Serializable.java 後,對象的序列化和反序列化都成功了,以下所示
serialize person success deserialize person success Person{id=1111, name='雙十一', sex=true} Process finished with exit code 0
4)序列化到文件中的信息,以下所示
三:serialVersionUID 的做用
實驗步驟:
1)先將 Person.java 類的對象序列化到 D://PersonInfo.text 文件中(在Person.java類中沒有顯示聲明 serialVersionUID)
2)爲Person.java 類添加一個年齡屬性
/** * 年齡 */ private int age;
3)反序列化 D://PersonInfo.text 文件中對象信息
4)執行結果以下(拋出了 java.io.InvalidClassException ):
Connected to the target VM, address: '127.0.0.1:51721', transport: 'socket' Disconnected from the target VM, address: '127.0.0.1:51721', transport: 'socket' java.io.InvalidClassException: com.godtrue.Person; local class incompatible: stream classdesc serialVersionUID = 2168487965208983906, local class serialVersionUID = 7553450974679663255 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) at com.godtrue.SerializeAndDeserialize.deserializePerson(SerializeAndDeserialize.java:63) at com.godtrue.SerializeAndDeserialize.main(SerializeAndDeserialize.java:28) Process finished with exit code 0
5)分析——看拋錯的日誌信息,引發這個問題的緣由是由於,序列化和反序列化信息中的 serialVersionUID 這個屬性的值不一致形成的,那問題來了 serialVersionUID 這個屬性哪裏來的?這個屬性的值又是哪裏來的?爲何同一個對象的序列化和反序列化信息中的有些信息不同呢?
5-1)serialVersionUID 這個屬性是JDK工具添加上去
5-2)serialVersionUID 這個屬性的值,若是沒有顯式指明,則會由JDK工具自動生成,若是顯式聲明瞭,則使用顯式聲明的
5-3)serialVersionUID 這個屬性的默認生成規則,和類中的信息有關,若是類有所改變,則會影響此屬性的值的生成
6)繼續試驗下,5)中的結論,試驗步驟以下:
6-1)先將Person.java中的 age 屬性去掉,而後顯式的聲明 private static final long serialVersionUID=1L;
6-2)而後,將 Person.java 類對應的對象信息,序列化到 D://PersonInfo.text 文件中
6-3)而後,將 Person.java 類的 age 屬性再添加回去
6-4)而後,將Person.java 類的對象信息反序列化爲對象,
6-5)試驗ok了,序列化和反序列化都沒有問題,只是反序列化後的對象信息中 age 屬性的值是默認屬性 0
7)繼續試驗,逆向的驗證一下反序列化,試驗步驟以下:
7-1)先將Person.java中的 age 屬性添加上,而後顯式的聲明 private static final long serialVersionUID=1L;
7-2)而後,將 Person.java 類對應的對象信息,序列化到 D://PersonInfo.text 文件中
7-3)而後,將 Person.java 類的 age 屬性再去掉
7-4)而後,將Person.java 類的對象信息反序列化爲對象,
7-5)試驗ok了,序列化和反序列化都沒有問題,只是,反序列化後的對象信息中沒有 age 屬性而已,(去掉後,反序列化固然是沒有的)
8)從上述的試驗中,咱們能夠發現一些有趣的事情
8-1)當 serialVersionUID 屬性的值,不一致時,即便是同一個類,他的序列化和反序列化先後的屬性沒有增減,也是不能正確反序列化的
8-2)當 serialVersionUID 屬性的值,一致時,同一個類,他的序列化和反序列化先後的屬性有增減,也能正確反序列化的
參考:
http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html
https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html
https://www.cnblogs.com/xdp-gacl/p/3777987.html
http://ju.outofmemory.cn/entry/298220
https://blog.csdn.net/qq_27093465/article/details/78544505
https://blog.csdn.net/jason_279/article/details/52947093
https://blog.csdn.net/u011784767/article/details/78156319?locationNum=2&fps=1
https://www.cnblogs.com/wangg-mail/p/4354709.html
https://blog.csdn.net/leixingbang1989/article/details/50556966
https://www.cnblogs.com/DSNFZ/articles/7618470.html
http://www.cnblogs.com/huhx/p/serializable.html
https://blog.csdn.net/so_geili/article/details/78931742
https://www.oschina.net/question/4873_23270
http://www.javashuo.com/article/p-rpfgmdnv-eb.html
https://www.cnblogs.com/gtaxmjld/p/4866931.htmlhttps://blog.csdn.net/zhangliao613/article/details/51086562