<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # Java 序列化 – 執行正確的序列化 > 原文: [https://howtodoinjava.com/java/serialization/a-mini-guide-for-implementing-serializable-interface-in-java/](https://howtodoinjava.com/java/serialization/a-mini-guide-for-implementing-serializable-interface-in-java/) **Java 序列化**允許將 Java 對象寫入文件系統以進行永久存儲,也可以將其寫入網絡以傳輸到其他應用程序。 Java 中的序列化是通過`Serializable`接口實現的。 Java [`Serializable`接口](https://docs.oracle.com/javase/6/docs/api/java/io/Serializable.html "Serializable interface")保證[可以序列化對象](https://howtodoinjava.com/java/serialization/custom-serialization-readobject-writeobject/)的能力。 此接口建議我們也使用`serialVersioUID`。 現在,即使您在應用程序類中同時使用了兩者,您是否知道**哪怕現在會破壞您的設計**? 讓我們確定類中將來的更改,這些更改將是**兼容的更改**,而其他類別將證明**不兼容的更改**。 ```java Table of contents 1\. Java serialization incompatible changes 2\. Java serialization compatible changes 3\. serialVersionUID 4\. readObject() and writeObject() methods 5\. More serialization best practices 6\. Sample class following serialization best practices 7\. Serialization and deserialization example ``` ## 1\. Java 序列化不兼容的更改 對類的不兼容更改是指不能保持互操作性的那些更改。 下面給出了在演化類時可能發生的不兼容更改(考慮默認序列化或[反序列化](https://howtodoinjava.com/java/serialization/how-deserialization-process-happen-in-java/)): 1. **刪除字段** – 如果在類中刪除了字段,則寫入的流將不包含其值。 當較早的類讀取流時,該字段的值將設置為默認值,因為流中沒有可用的值。 但是,此默認值可能會不利地損害早期版本履行其契約的能力。 2. **將類上移或下移** – 不允許這樣做,因為流中的數據顯示順序錯誤。 3. **將非靜態字段更改為靜態或將非瞬態字段更改為瞬態** – 當依賴默認序列化時,此更改等效于從類中刪除字段。 該版本的類不會將該數據寫入流,因此該類的早期版本將無法讀取該數據。 與刪除字段時一樣,早期版本的字段將被初始化為默認值,這可能導致類以意外方式失敗。 4. **更改原始字段的聲明類型** – 該類的每個版本都使用其聲明類型寫入數據。 嘗試讀取該字段的早期版本的類將失敗,因為流中的數據類型與該字段的類型不匹配。 5. **更改`writeObject`或`readObject`方法,使其不再寫入或讀取默認字段數據**,或對其進行更改,以使其嘗試寫入或讀取以前的版本時不進行讀取。 默認字段數據必須一致地出現在流中或不出現在流中。 6. **將類從`Serializable`更改為`Externalizable`,反之亦然**是不兼容的更改,因為流將包含與可用類的實現不兼容的數據。 7. **將類從非枚舉類型更改為枚舉類型,反之亦然**,因為流將包含與可用類的實現不兼容的數據。 8. 刪除[`Serializable`](https://docs.oracle.com/javase/6/docs/api/java/io/Serializable.html "Serializable interface")或`Externalizable`是不兼容的更改,因為在編寫時它將不再提供該類的較早版本所需的字段。 9. **如果該行為會產生與該類的任何舊版本不兼容的對象,則將`writeReplace`或`readResolve`方法添加到類是不兼容的**。 ## 2\. Java 序列化兼容更改 1. **添加字段** – 當重構的類具有流中未出現的字段時,對象中的該字段將被初始化為其類型的默認值。 如果需要特定于類的初始化,則該類可以提供一個`readObject`方法,該方法可以將字段初始化為非默認值。 2. **添加類** – 流將包含流中每個對象的類型層次結構。 將流中的此層次結構與當前類進行比較可以檢測到其他類。 由于流中沒有用于初始化對象的信息,因此該類的字段將被初始化為默認值。 3. **刪除類** – 將流中的類層次結構與當前類的層次結構進行比較可以檢測到某個類已被刪除。 在這種情況下,從該流中讀取與該類相對應的字段和對象。 [原始字段](https://howtodoinjava.com/java/basics/primitive-data-types-in-java/)被丟棄,但是創建了由已刪除類引用的對象,因為可以在流中稍后引用它們。 當流被垃圾回收或重置時,它們將被垃圾回收。 4. **添加`writeObject`/`readObject`方法** – 如果讀取流的版本具有這些方法,則通常希望`readObject`讀取通過默認序列化寫入流中的所需數據。 在讀取任何可選數據之前,應先調用`defaultReadObject`。 通常,`writeObject`方法將調用`defaultWriteObject`寫入所需的數據,然后再寫入可選數據。 5. **刪除`writeObject`/`readObject`方法** – 如果讀取流的類沒有這些方法,則默認情況下將序列化讀取所需數據,而可選數據將被丟棄。 6. **添加`java.io.Serializable`** – 這等同于添加類型。 該類的流中將沒有任何值,因此其字段將被初始化為默認值。 對子類化不可序列化類的支持要求該類的超類型具有無參構造器,并且該類本身將被初始化為默認值。 如果無參構造器不可用,則拋出`InvalidClassException`。 7. **更改對字段的訪問** – 公共,包,保護和私有的[訪問修飾符](https://howtodoinjava.com/oops/java-access-modifiers/)對序列化為字段分配值的能力沒有影響。 8. **將字段從靜態更改為非靜態,或將瞬態更改為非瞬態** – 當依賴默認序列化來計算可序列化字段時,此更改等效于將字段添加到類中。 新字段將被寫入流,但是較早的類將忽略該值,因為序列化不會為[靜態](https://howtodoinjava.com/java/basics/java-static-keyword/)或[瞬態](https://howtodoinjava.com/java/basics/transient-keyword-in-java-with-real-time-example/)字段分配值。 ## 3\. `serialVersionUID` `serialVersionUID`是`Serializable`類的通用版本標識符。 反序列化使用此數字來確保已加載的類與序列化的對象完全對應。 如果找不到匹配項,則拋出`InvalidClassException`。 1. **始終將其包含為字段**,例如:` private static final long serialVersionUID = 7526472295622776147L;`,即使在類的第一個版本中也要包含此字段,以提醒其重要性。 2. **請勿在以后的版本中更改此字段的值,除非您有意對類進行**更改,以使其與舊的序列化對象不兼容。 如果需要,請遵循上述給定的準則。 ## 4\. `readObject`和`writeObject`方法 1. 反序列化必須視為任何構造器:**在反序列化**結束時驗證對象狀態 – 這意味著`readObject`幾乎應始終在`Serializable`類中實現,以便執行此驗證。 2. 如果構造器**為可變對象字段制作防御性副本**,則必須讀取對象。 ## 5\. 更多序列化最佳實踐 1. 使用 javadoc 的`@serial`標記表示可序列化字段。 2. `.ser`擴展名通常用于表示序列化對象的文件。 3. 沒有靜態或瞬態字段接受默認序列化。 4. 除非必要,否則可擴展類不應是可序列化的。 5. 內部類很少(如果有的話)實現`Serializable`。 6. 容器類通常應遵循`Hashtable`的樣式,該樣式通過存儲鍵和值來實現`Serializable`,而不是大型哈希表數據結構。 ## 6\. 遵循序列化最佳實踐的示例類 ```java package staticTest; import java.io.Serializable; import java.text.StringCharacterIterator; import java.util.*; import java.io.*; public final class UserDetails implements Serializable { /** * This constructor requires all fields * * @param aFirstName * contains only letters, spaces, and apostrophes. * @param aLastName * contains only letters, spaces, and apostrophes. * @param aAccountNumber * is non-negative. * @param aDateOpened * has a non-negative number of milliseconds. */ public UserDetails(String aFirstName, String aLastName, int aAccountNumber, Date aDateOpened) { super(); setFirstName(aFirstName); setLastName(aLastName); setAccountNumber(aAccountNumber); setDateOpened(aDateOpened); // there is no need here to call verifyUserDetails. } // The default constructor public UserDetails() { this("FirstName", "LastName", 0, new Date(System.currentTimeMillis())); } public final String getFirstName() { return fFirstName; } public final String getLastName() { return fLastName; } public final int getAccountNumber() { return fAccountNumber; } /** * Returns a defensive copy of the field so that no one can change this * field. */ public final Date getDateOpened() { return new Date(fDateOpened.getTime()); } /** * Names must contain only letters, spaces, and apostrophes. Validate before * setting field to new value. * * @throws IllegalArgumentException * if the new value is not acceptable. */ public final void setFirstName(String aNewFirstName) { verifyNameProperty(aNewFirstName); fFirstName = aNewFirstName; } /** * Names must contain only letters, spaces, and apostrophes. Validate before * setting field to new value. * * @throws IllegalArgumentException * if the new value is not acceptable. */ public final void setLastName(String aNewLastName) { verifyNameProperty(aNewLastName); fLastName = aNewLastName; } /** * Validate before setting field to new value. * * @throws IllegalArgumentException * if the new value is not acceptable. */ public final void setAccountNumber(int aNewAccountNumber) { validateAccountNumber(aNewAccountNumber); fAccountNumber = aNewAccountNumber; } public final void setDateOpened(Date aNewDate) { // make a defensive copy of the mutable date object Date newDate = new Date(aNewDate.getTime()); validateAccountOpenDate(newDate); fDateOpened = newDate; } /** * The client's first name. * * @serial */ private String fFirstName; /** * The client's last name. * * @serial */ private String fLastName; /** * The client's account number. * * @serial */ private int fAccountNumber; /** * The date the account was opened. * * @serial */ private Date fDateOpened; /** * Determines if a de-serialized file is compatible with this class. * Included here as a reminder of its importance. */ private static final long serialVersionUID = 7526471155622776147L; /** * Verify that all fields of this object take permissible values * * @throws IllegalArgumentException * if any field takes an unpermitted value. */ private void verifyUserDetails() { validateAccountNumber(fAccountNumber); verifyNameProperty(fFirstName); verifyNameProperty(fLastName); validateAccountOpenDate(fDateOpened); } /** * Ensure names contain only letters, spaces, and apostrophes. * * @throws IllegalArgumentException * if field takes an unpermitted value. */ private void verifyNameProperty(String aName) { boolean nameHasContent = (aName != null) && (!aName.equals("")); if (!nameHasContent) { throw new IllegalArgumentException( "Names must be non-null and non-empty."); } StringCharacterIterator iterator = new StringCharacterIterator(aName); char character = iterator.current(); while (character != StringCharacterIterator.DONE) { boolean isValidChar = (Character.isLetter(character) || Character.isSpaceChar(character) || character == '''); if (isValidChar) { // do nothing } else { String message = "Names can contain only letters, spaces, and apostrophes."; throw new IllegalArgumentException(message); } character = iterator.next(); } } /** * AccountNumber must be non-negative. * * @throws IllegalArgumentException * if field takes an unpermitted value. */ private void validateAccountNumber(int aAccountNumber) { if (aAccountNumber < 0) { String message = "Account Number must be greater than or equal to 0."; throw new IllegalArgumentException(message); } } /** * DateOpened must be after 1970. * * @throws IllegalArgumentException * if field takes an unpermitted value. */ private void validateAccountOpenDate(Date aDateOpened) { if (aDateOpened.getTime() < 0) { throw new IllegalArgumentException( "Date Opened must be after 1970."); } } /** * Always treat deserialization as a full-blown constructor, by validating * the final state of the de-serialized object. */ private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { // always perform the default deserialization first aInputStream.defaultReadObject(); // make defensive copy of the mutable Date field fDateOpened = new Date(fDateOpened.getTime()); // ensure that object state has not been corrupted or tampered with // malicious code verifyUserDetails(); } /** * This is the default implementation of writeObject. Customise if * necessary. */ private void writeObject(ObjectOutputStream aOutputStream) throws IOException { // perform the default serialization for all non-transient, non-static // fields aOutputStream.defaultWriteObject(); } } ``` 現在讓我們看看如何在 Java 中進行序列化和反序列化。 ## 序列化和反序列化示例 ```java package serializationTest; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Calendar; import java.util.Date; public class TestUserDetails { public static void main(String[] args) { // Create new UserDetails object UserDetails myDetails = new UserDetails("Lokesh", "Gupta", 102825, new Date(Calendar.getInstance().getTimeInMillis())); // Serialization code try { FileOutputStream fileOut = new FileOutputStream("userDetails.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(myDetails); out.close(); fileOut.close(); } catch (IOException i) { i.printStackTrace(); } // deserialization code @SuppressWarnings("unused") UserDetails deserializedUserDetails = null; try { FileInputStream fileIn = new FileInputStream("userDetails.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); deserializedUserDetails = (UserDetails) in.readObject(); in.close(); fileIn.close(); // verify the object state System.out.println(deserializedUserDetails.getFirstName()); System.out.println(deserializedUserDetails.getLastName()); System.out.println(deserializedUserDetails.getAccountNumber()); System.out.println(deserializedUserDetails.getDateOpened()); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } } } Output: Lokesh Gupta 102825 Wed Nov 21 15:06:34 GMT+05:30 2012 ``` 參考文獻: [http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html](https://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html "serialization spec")
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看