一.目的:
网络传输对象,数据库存储对象,文件存储对象时都需要实现serializable
序列化id使用:
serialVersionUID,反序列化时如果这个id与序列化时id不同会报错:
java.io.InvalidClassException: jvm.Product; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
这样可以使之后反序列化程序在使用这个旧的序列化对象时,抛错。
二.注意点:
1.静态对象及transient不序列化,序列化仅针对对象变量
2.若子类有实现serializable接口,父类没有,在序列化时只会序列化子类相关变量
原因:如果这时父类也实现,会导致所有相关父类都序列化。
3.相反,若一个父类已实现serializable,则序列化子类时,父类及子类相关的变量都会序列化。
即序列化具有继承特性。
4.在反序列化时,父类必须要有默认构造器,否则会报错
no valid constructor
5.有些字段序列化时要求加密怎么处理
对象序列化过程:
a.虚拟机尝试调用要序列化类里的 readObject方法(用于个性化),没有则调用ObjectOutputStream的defaultReadFields
对象反序列化过程:
a.虚拟机尝试调用反序列化的writeObject方法,没有则调用ObjectInputStream的defaultWriteFields
所有可以利用这两个个性化方法,这也是设计时根据业务场景,提供一些方法让使用方能够个性化。
6.java序列化的存储优化
当写入两次同一个对象时,对应保存的对象内存只会有一份,而只会增加引用。
这样做目的是为了节省存储空间。
三.加密字段使用
bankCard类通过writeObject方法来 加密银行卡号,readObject来解密银行卡号。
public class BankCard implements Serializable {
private static final long serialVersionUID = 1L;
private String cardNo;
private void writeObject(ObjectOutputStream out) {
try {
/**
* 1.验证
*/
if (cardNo == "") {
return;
}
/**
* 2.加密
*/
PutField putField = out.putFields();
long cardNoLong = Long.valueOf(cardNo);
cardNoLong = cardNoLong + 1;
String encryptCardNo = String.valueOf(cardNoLong);
putField.put("cardNo", encryptCardNo);
/**
* 3.一定要调用这个,将相应字段序列化到目的地
*/
out.writeFields();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream in) {
try {
/**
* 1.校验
*/
GetField getField = in.readFields();
String cardNoBefore = (String) getField.get("cardNo", "");
if (cardNoBefore == "") {
return;
}
System.out.println("=解密前:" + cardNoBefore);
/**
* 2.解密
*/
long cardNoLong = Long.valueOf(cardNoBefore);
cardNoLong = cardNoLong - 1;
this.cardNo = String.valueOf(cardNoLong);
System.out.println("=解密后:" + cardNo);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
}