POJO(plain ordinary javaobject):“簡單普通Java對象”
①有一些屬性爲private
②針對每一個屬性定義get
和set
方法接口
③沒有任何類繼承,也沒有實現任何接口,更沒有其他框架入侵的java對象
示例:
public class BasicInfoVo {
private String orderId;
private Integer uid;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
}
JavaBean:是一種Java語言編寫的可重用的組件
①所有屬性爲private
②有一個無參構造器
③這個類屬性使用getter
和setter
來訪問,其他方法遵從標準命名規範
④這個類的接口是可序列化的,實現serializable
接口
示例:
public class UserInfo implements java.io.Serializable{
//實現serializable接口。
private static final long serialVersionUID = 1L;
private String name;
private int age;
//無參構造器
public UserInfo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//javabean當中可以有其它的方法
public void userInfoPrint(){
System.out.println("");
}
}
區別:
①POJO
其實是比javabean
更純淨的簡單類或接口。POJO
嚴格地遵守簡單對象的概念,而一些JavaBean
中往往會封裝一些簡單邏輯。
②POJO
主要用於數據的臨時傳遞,它只能裝載數據, 作爲數據存儲的載體,而不具有業務邏輯處理的能力。
③Javabean
雖然數據的獲取與POJO
一樣,但是javabean
當中可以有其它的方法。
備註:
Serializable
接口屬於標誌接口,這個接口沒有任何的方法定義,它僅僅只是標記某個類能被序列化和反序列化。(接口不包含任何方法)
一、什麼是序列化?
【將對象的狀態信息轉換爲可以存儲或傳輸的形式的過程,在序列化期間,對象將其當前狀態寫入到臨時存儲區或持久性存儲區,之後,便可以通過從存儲區中讀取或反序列化對象的狀態信息,來重新創建該對象】
序列化將數據分解成字節流,以便存儲在文件中或在網絡上傳輸。
反序列化就是打開字節流並重構對象。
二、什麼情況下需要序列化?
【當我們需要把對象的狀態信息通過網絡進行傳輸,或者需要將對象的狀態信息持久化,以便將來使用時都需要把對象進行序列化】
三、Serializable
主要用來支持兩種主要的特性:
1、Java
的RMI(remote method invocation
),RMI
允許像在本機上一樣操作遠程機器上的對象,當發送消息給遠程對象時,就需要用到序列化機制來發送參數和接受返回值。
2、Java
的JavaBean
,Bean
的狀態信息通常是在設計時配置的,Bean
的狀態信息必須被保存下來,以便當程序運行時能恢復這些狀態信息,這也需要序Serializable
機制。
只需要瞭解被序列化的類需要實現 Serializable
接口,使用 ObjectInputStream
和 ObjectOutputStream
進行對象的讀寫。
四、常出現的情景
1、兩個類A B
代碼完全相同,試圖通過網絡傳遞對象數據,A
端將對象 C
序列化爲二進制數據再傳給 B
,B
反序列化得到 C
。但一直反序列化不成功。
解決:虛擬機是否允許反序列化,不僅取決於類路徑和功能代碼是否一致,一個非常重要的一點是兩個類的序列化 ID
是否一致(就是 private static final long serialVersionUID = 1L
)。雖然兩個類的功能代碼完全一致,但是序列化 ID
不同,他們無法相互序列化和反序列化。
序列化 ID 在 Eclipse 下提供了兩種生成策略,
(1)固定的 1L(一般這麼做。可以確保代碼一致時反序列化成功)
private static final long serialVersionUID = 1L;
(2)一個是隨機生成一個不重複的 long
類型數據(作用是:通過改變序列化 ID
可以用來限制某些用戶的使用。)
2、Facade
外觀模式:
Client
端通過 Façade Object
纔可以與業務邏輯對象進行交互。而客戶端的 Façade Object
不能直接由 Client
生成,而是需要 Server
端生成,然後序列化後通過網絡將二進制對象數據傳給 Client
,Client
負責反序列化得到 Façade
對象。
即 服務端(生成Facade object
)序列化 ----->客戶端(反序列化,得到Facade
對象,可用其與業務邏輯對象交互)
該模式可以使得 Client
端程序的使用需要服務器端的許可,同時 Client
端和服務器端的 Façade Object
類需要保持一致。當服務器端想要進行版本更新時,只要將服務器端的 Façade Object
類的序列化 ID
再次生成,當 Client
端反序列化 Façade Object
就會失敗,也就是強制 Client
端從服務器端獲取最新程序。
3、一個類實現了序列化,並且有其靜態變量。
但序列化保存的是對象的狀態,靜態變量屬於類的狀態,因此 序列化並不保存靜態變量。
4、父類的序列化與transient
:
父類不實現序列化,子類實現序列化,則將子類對象反序列化後輸出父類定義的某變量的數值,該變量數值與序列化時的數值不同。
原因:一個java
對象的構造必須先有父對象,再有子對象,反序列化也是,父類沒有實現序列化,則虛擬機不會序列化父類。
解決:
a、父類也實現序列化
b、爲父類創造默認的無參的構造方法,在父類無參構造方法中對父類進行變量初始化。則反序列化時,構造父對象時只能調用父類無參構造函數爲默認父對象,如果不先在無參構造函數中初始化變量,則父類變量值都會是默認聲明值,如0,null
。
所以,使變量不被序列化的方法有:
a、Transient
關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化後,transient
變量的值被設爲初始值,如 int
型的是 0
,對象型的是 null
。
b、可以將不需要被序列化的字段抽取出來放到父類中,子類實現序列化,父類不實現,則父類的字段數據將不會被序列化。
5、對敏感字段加密
服務端給客戶端發送序列化對象數據,並在序列化時進行加密,客戶端用解密密鑰,在反序列化時,對密碼讀取,保證了序列化對象的數據安全。
在序列化過程中,虛擬機會試圖調用對象類裏的 writeObject
和 readObject
方法,進行用戶自定義的序列化和反序列化,如果沒有這樣的方法,則默認調用是 ObjectOutputStream
的 defaultWriteObject
方法以及 ObjectInputStream
的 defaultReadObject
方法。用戶自定義的 writeObject
和 readObject
方法可以允許用戶控制序列化的過程,比如可以在序列化的過程中動態改變序列化的數值。基於這個原理,可以在實際應用中得到使用,用於敏感字段的加密工作。
如RMI
技術。