Java中的序列化是通过Serializable接口实现的。 Java Serializable接口保证了序列化对象的能力。 此接口也建议我们使用serialVersioUID。
现在,即使您在应用程序类中同时使用了序列化和反序列化,您是否知道也有可能已经破坏您的设计呢? 下面让我们确定类中将来的更改,这些更改将是兼容的更改,而其他更改将证明是不兼容的更改。
对类的不兼容更改是指不能保持互操作性的那些更改。 下面给出了在演化类时可能发生的不兼容更改(考虑默认的序列化或反序列化):
serialVersionUID是Serializable类的通用版本标识符。 反序列化使用此数字来确保已加载的类与序列化的对象完全对应。 如果找不到匹配项,则抛出InvalidClassException。
package staticTest;
import java.io.Serializable;
import java.text.StringCharacterIterator;
import java.util.*;
import java.io.*;
public final class UserDetails implements Serializable {
/**
* 此构造函数需要所有字段
*
* @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.
}
// 默认构造函数
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());
}
/**
* 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中进行序列化和反序列化。
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();
}
}
}
执行示例,得到结果如下:
Lokesh
Gupta
102825
Wed Nov 21 15:06:34 GMT+05:30 2012
http://blog.xqlee.com/article/688.html