Еще несколько вещей которые нужно помнить
  1. Используйте @serial тэг чтобы обозначать сериализуемые поля.
  2. Расширение .ser обычно используется для файлов с сериализованными объектами.
  3. Статические и несериализуемые поля не сериализуются
  4. Расширяемые классы не должны быть сериализуемыми, кроме как при необходимости.
  5. Внутренние классы редко, если вообще, могут быть сериализуемыми.
  6. Классы контейнеры должны обычно следовать стилю Hashtable который реализует интерфейс Serializable с парами ключ-значение в противоположность большим структурам данных.
Пример реализации package staticTest; import java.io.Serializable; import java.text.StringCharacterIterator; import java.util.*; import java.io.*; public final class UserDetails implements Serializable { /** * Конструктор инициализирующий все поля класса переданными значениями * * @param aFirstName * может содержать буквы, пробелы и апострофы * @param aLastName * может содержать буквы, пробелы и апострофы * @param aAccountNumber * положительное число * @param aDateOpened * положительное количество миллисекунд */ public UserDetails(String aFirstName, String aLastName, int aAccountNumber, Date aDateOpened) { super(); setFirstName(aFirstName); setLastName(aLastName); setAccountNumber(aAccountNumber); setDateOpened(aDateOpened); // нет необходимости вызывать verifyUserDetails. } // конструктор по умолчанию 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; } /** * Возвращает не указатель на тот же объект а новый объект инициализированный значением из нашего поля, чтобы никто его потом не поменял */ public final Date getDateOpened() { return new Date(fDateOpened.getTime()); } /** * Имена могут содержать только буквы, пробелы и апострофы. Необходимо проверить значение * прежде чем устанавливать его полю. * * @throws IllegalArgumentException * если значение недопустимо */ public final void setFirstName(String aNewFirstName) { verifyNameProperty(aNewFirstName); fFirstName = aNewFirstName; } /** * Имена могут содержать только буквы, пробелы и апострофы. Необходимо проверить значение * прежде чем устанавливать его полю. * * @throws IllegalArgumentException * если значение недопустимо */ public final void setLastName(String aNewLastName) { verifyNameProperty(aNewLastName); fLastName = aNewLastName; } /** * Необходимо проверить значение прежде чем устанавливать его полю * * @throws IllegalArgumentException * если значение недопустимо */ public final void setAccountNumber(int aNewAccountNumber) { validateAccountNumber(aNewAccountNumber); fAccountNumber = aNewAccountNumber; } public final void setDateOpened(Date aNewDate) { // создает новый объект, и инициализирует его значением поля Date newDate = new Date(aNewDate.getTime()); validateAccountOpenDate(newDate); fDateOpened = newDate; } /** * Имя клиента * * @serial */ private String fFirstName; /** * Фамилия клиента * * @serial */ private String fLastName; /** * Номер счета клиента * * @serial */ private int fAccountNumber; /** * Дата открытия счета * * @serial */ private Date fDateOpened; /** * Определяет, совместим ли файл с сериализованными данными с этим объектом * Здесь включено как напоминание о важности данного поля */ private static final long serialVersionUID = 7526471155622776147L; /** * Проверка того, что все поля объекта имеют допустимые значения * * @throws IllegalArgumentException * если любое поле имеет недопустимое значение */ private void verifyUserDetails() { validateAccountNumber(fAccountNumber); verifyNameProperty(fFirstName); verifyNameProperty(fLastName); validateAccountOpenDate(fDateOpened); } /** * Проверяет что имя содержит только буквы, пробелы и апострофы * * @throws IllegalArgumentException * если поле имеет недопустимое значение */ private void verifyNameProperty(String aName) { boolean nameHasContent = (aName != null) &amp;amp;amp;&amp;amp;amp; (!aName.equals("")); if (!nameHasContent) { throw new IllegalArgumentException( "Имя должно быть инициализировано и не должно быть пустым"); } StringCharacterIterator iterator = new StringCharacterIterator(aName); char character = iterator.current(); while (character != StringCharacterIterator.DONE) { boolean isValidChar = (Character.isLetter(character) || Character.isSpaceChar(character) || character == '''); if (isValidChar) { // все в порядке } else { String message = "Имя может содержать только буквы, пробелы и апострофы"; throw new IllegalArgumentException(message); } character = iterator.next(); } } /** * Номер счета должен быть положительным * * @throws IllegalArgumentException * если поле имеет недопустимое значение */ private void validateAccountNumber(int aAccountNumber) { if (aAccountNumber < 0) { String message = "Номер счета должен быть больше либо равен 0"; throw new IllegalArgumentException(message); } } /** * Дата должна быть позже 1970 * * @throws IllegalArgumentException * если поле имеет недопустимое значение */ private void validateAccountOpenDate(Date aDateOpened) { if (aDateOpened.getTime() < 0) { throw new IllegalArgumentException( "Дата открытия счета должна быть позже 1970"); } } /** * Всегда воспринимайте десериализацию как полноценный конструктор, с проверкой финального состояния десериализованного объекта */ private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { // первым делом всегда проводить десериализацию по умолчанию aInputStream.defaultReadObject(); // создавайте новый объект для изменяемого поля fDateOpened = new Date(fDateOpened.getTime()); //проверьте, что состояние объекта не повреждено и не испорчено verifyUserDetails(); } /** * Реализация по умолчанию, измените если необходимо */ private void writeObject(ObjectOutputStream aOutputStream) throws IOException { // производит сериализацию по умолчанию, для всех нестатических и сериализуемых полей aOutputStream.defaultWriteObject(); } } А сейчас посмотрим как будет выглядеть сериализация и десериализация 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) { // Создаем новый объект UserDetails UserDetails myDetails = new UserDetails("Lokesh", "Gupta", 102825, new Date(Calendar.getInstance().getTimeInMillis())); // Сериализация try { FileOutputStream fileOut = new FileOutputStream("userDetails.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(myDetails); out.close(); fileOut.close(); } catch (IOException i) { i.printStackTrace(); } // Десериализация @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(); // Проверка статуса объекта 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(); } } } Вывод программы:
 
Lokesh
Gupta
102825
Wed Nov 21 15:06:34 GMT+05:30 2012
Оригинал статьи