Java Bean類實現Serializable介面的原因
簡單來說序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化,流的概念這裡不用多說(就是I/O),我們可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間!在對物件流進行讀寫操作時會引發一些問題,而序列化機制正是用來解決這些問題的!
它面向那些實現了Serializable介面的物件,可將它們轉換成一系列位元組,並可在以後完全恢復回原來的樣子。這一過程亦可通過網路進行。這意味著序列化機制能自動補償作業系統間的差異。換句話說,可以先在Windows機器上建立一個物件,對其序列化,然後通過網路發給一臺Unix機器,然後在那裡準確無誤地重新“裝配”。不必關心資料在不同機器上如何表示,也不必關心位元組的順序或者其他任何細節。
那麼我們在什麼情況下需要使用到Serializable介面呢??
1、當你想把的記憶體中的物件狀態儲存到一個檔案中或者資料庫中時候;
2、當你想用套接字在網路上傳送物件的時候;
3、當你想通過RMI傳輸物件的時候;
下面我們由一個問題的引出當我們想把的記憶體中的物件狀態儲存到一個檔案中或者資料庫中時候,Serializable介面能幫我們做些什麼?
如上所述,讀寫物件會有什麼問題呢?比如:我要將物件寫入一個磁碟檔案而後再將其讀出來會有什麼問題嗎?別急,其中一個最大的問題就是物件引用!舉個例子來說:假如我有兩個類,分別是A和B,B類中含有一個指向A類物件的引用,現在我們對兩個類進行例項化{ A a = new A(); B b = new B(); },這時在記憶體中實際上分配了兩個空間,一個儲存物件a,一個儲存物件b,接下來我們想將它們寫入到磁碟的一個檔案中去,就在寫入檔案時出現了問題!因為物件b包含對物件a的引用,所以系統會自動的將a的資料複製一份到b中,這樣的話當我們從檔案中恢復物件時(也就是重新載入到記憶體中)時,記憶體分配了三個空間,而物件a同時在記憶體中存在兩份,想一想後果吧,如果我想修改物件a的資料的話,那不是還要搜尋它的每一份拷貝來達到物件資料的一致性,這不是我們所希望的!
以下序列化機制的解決方案:
1.儲存到磁碟的所有物件都獲得一個序列號(1, 2, 3等等)
2.當要儲存一個物件時,先檢查該物件是否被儲存了。
3.如果以前儲存過,只需寫入"與已經儲存的具有序列號x的物件相同"的標記,否則,儲存該物件通過以上的步驟序列化機制解決了物件引用的問題!
在對物件進行例項化的過程中相關注意事項
a)序列化時,只對物件的狀態進行儲存,而不管物件的方法;
b)當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable介面;
c)當一個物件的例項變數引用其他物件,序列化該物件時也把引用物件進行序列化;
d)並非所有的物件都可以序列化,至於為什麼不可以,有很多原因了,比如:
1.安全方面的原因,比如一個物件擁有private,public等field,對於一個要傳輸的物件,比如寫到檔案,或者進行RMI傳輸 等等,在序列化進行傳輸的過程中,這個物件的private等域是不受保護的。
2. 資源分配方面的原因,比如socket,thread類,如果可以序列化,進行傳輸或者儲存,也無法對他們進行重新的資源分配,而且,也是沒有必要這樣實現。
序列化的實現
將需要被序列化的類實現Serializable介面,該介面沒有需要實現的方法,implements Serializable只是為了標註該物件是可被序列化的,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(物件流)物件,接著,使用ObjectOutputStream物件的writeObject(Object obj)方法就可以將引數為obj的物件寫出(即儲存其狀態),要恢復的話則用輸入流呼叫ObjectInputStream的readObject()方法。
例子:
Employee類:
public class Employee implements Serializable{
private String name;
private double salary;
public Employee(String name, double salary) {
super();
// TODO Auto-generated constructor stub
this.name = name;
this.salary = salary;
}
public void raiseSalary(double byPercent){
double temp = salary * byPercent / 100;
salary += temp;
}
public String toString() {
// TODO Auto-generated method stub
return getClass().getName() +
"[ Name = " + name + ", salary = " + salary +"]";
}
}
Manager類:
public class Manager extends Employee {
private Employee secretary;
public Manager(String name, double salary) {
super(name, salary);
// TODO Auto-generated constructor stub
secretary = null;
}
public Employee getSecretary() {
return secretary;
}
public void setSecretary(Employee secretary) {
this.secretary = secretary;
}
public String toString() {
// TODO Auto-generated method stub
return super.toString() + "[ secretary = " + secretary +"]";
}
}
MainTest類:
public class MainTest {
public static void main(String[] args){
Employee employee = new Employee("LiLei", 1000);
Manager manager1 = new Manager("Jim", 20000);
manager1.setSecretary(employee);
Employee[] staff = new Employee[2];
staff[0] = employee;
staff[1] = manager1;
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.dat"));
oos.writeObject(staff);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.dat"));
Employee[] newStaff = (Employee[])ois.readObject();
ois.close();
newStaff[0].raiseSalary(1000);
for(int i=0; i<newStaff.length; i++)
System.out.println(newStaff[i]);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
修改預設的序列化機制
在序列化的過程中,有些資料欄位我們不想將其序列化,對於此類欄位我們只需要在定義時給它加上transient關鍵字即可,對於transient欄位序列化機制會跳過不會將其寫入檔案,當然也不可被恢復。但有時我們想將某一欄位序列化,但它在SDK中的定義卻是不可序列化的型別,這樣的話我們也必須把他標註為transient,可是不能寫入又怎麼恢復呢?好在序列化機制為包含這種特殊問題的類提供瞭如下的方法定義:
private void readObject(ObjectInputStream in) throws
IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out) throws
IOException;
(注:這些方法定義時必須是私有的,因為不需要你顯示呼叫,序列化機制會自動呼叫的)
使用以上方法我們可以手動對那些你又想序列化又不可以被序列化的資料欄位進行寫出和讀入操作。
下面是一個典型的例子,java.awt.geom包中的Point2D.Double類就是不可序列化的,因為該類沒有實現Serializable介面,在我的例子中將把它當作LabeledPoint類中的一個數據欄位,並演示如何將其序列化
LabeledPoint類:
public class LabeledPoint implements Serializable{
private String label;
transient private Point2D.Double point;
public LabeledPoint(String label, double x, double y) {
super();
// TODO Auto-generated constructor stub
this.label = label;
this.point = new Point2D.Double(x,y);
}
private void writeObject(ObjectOutputStream oos)throws IOException{
oos.defaultWriteObject();
oos.writeDouble(point.getX());
oos.writeDouble(point.getY());
}
private void readObject(ObjectInputStream ois)throws IOException, ClassNotFoundException{
ois.defaultReadObject();
double x = ois.readDouble() + 1.0;
double y = ois.readDouble() + 1.0;
point = new Point2D.Double(x,y);
}
public String toString() {
// TODO Auto-generated method stub
return getClass().getName() + "[ Label = " + label + ", point.getX() = "
+ point.getX() + ", point.getY() = " + point.getY() + "]";
}
}
transientTest類:
public class transientTest {
public static void main(String[] args){
LabeledPoint label = new LabeledPoint("Book", 5.0, 5.0);
try{
System.out.println("before:\n" + label);
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("c:\\label.txt"));
oos.writeObject(label);
oos.close();
System.out.println("after:\n" + label);
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("c:\\label.txt"));
LabeledPoint label1 = (LabeledPoint)ois.readObject();
ois.close();
System.out.println("after add 1.0:\n" + label);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
就其本身來說,物件的序列化是非常有趣的,因為利用它可以實現“有限持久化”。請記住“持久化”意味著物件的“生存時間”並不取決於程式是否正在執行——它存在或“生存”於程式的每一次呼叫之間。通過序列化一個物件,將其寫入磁碟,以後在程式重新呼叫時重新恢復那個物件,就能圓滿實現一種“持久”效果。之所以稱其為“有限”,是因為不能用某種“persistent”(持久)關鍵字簡單地定義一個物件,並讓系統自動照看其他所有細節問題(儘管將來可能成為現實)。相反,必須在自己的程式中明確地序列化和組裝物件。
語言裡增加了物件序列化的概念後,可提供對兩種主要特性的支援。Java 1.1的“遠端方法呼叫”(RMI)使本來存在於其他機器的物件可以表現出好像就在本地機器上的行為。將訊息發給遠端物件時,需要通過物件序列化來傳輸引數和返回值。
物件的序列化也是Java Beans必需的,後者由Java 1.1引入。使用一個Bean時,它的狀態資訊通常在設計期間配置好。程式啟動以後,這種狀態資訊必須儲存下來,以便程式啟動以後恢復;具體工作由物件序列化完成。
物件的序列化處理非常簡單,只需物件實現了Serializable介面即可(該介面僅是一個標記,沒有方法)。在Java 1.1中,許多標準庫類都發生了改變,以便能夠序列化——其中包括用於基本資料型別的全部封裝器、所有集合類以及其他許多東西。甚至Class物件也可以序列化。
為序列化一個物件,首先要建立某些OutputStream物件,然後將其封裝到ObjectOutputStream物件內。此時,只需呼叫writeObject()即可完成物件的序列化,並將其傳送給OutputStream。相反的過程是將一個InputStream封裝到ObjectInputStream內,然後呼叫readObject()。和往常一樣,我們最後獲得的是指向一個上溯造型Object的控制代碼,所以必須下溯造型,以便能夠直接設定。
物件序列化特別“聰明”的一個地方是它不僅儲存了物件的“全景圖”,而且能追蹤物件內包含的所有控制代碼並儲存那些物件;接著又能對每個物件內包含的控制代碼進行追蹤;以此類推。我們有時將這種情況稱為“物件網”,單個物件可與之建立連線。而且它還包含了物件的控制代碼陣列以及成員物件。若必須自行操縱一套物件序列化機制,那麼在程式碼裡追蹤所有這些連結時可能會顯得非常麻煩。在另一方面,由於Java物件的序列化似乎找不出什麼缺點,所以請儘量不要自己動手,讓它用優化的演算法自動維護整個物件網。下面這個例子對序列化機制進行了測試。它建立了許多連結物件的一個“Worm”(蠕蟲),每個物件都與Worm中的下一段連結,同時又與屬於不同類(Data)的物件控制代碼陣列連結:
Data類:
public class Data implements Serializable {
private int i;
Data(int x) {
i = x;
}
public String toString() {
return Integer.toString(i);
}
}
Worm類:public class Worm implements Serializable{
private static int r() {
return (int) (Math.random() * 10);
}
private Data[] d = { new Data(r()), new Data(r()), new Data(r()) };
private Worm next;
private char c;
// Value of i == number of segments
Worm(int i, char x) {
System.out.println(" Worm constructor: " + i);
c = x;
if (--i > 0)
next = new Worm(i, (char) (x + 1));
}
Worm() {
System.out.println("Default constructor ");
}
public String toString() {
String s = ": " + c + "( ";
for (int i = 0; i < d.length; i++)
s += d[i].toString();
s += ") ";
if (next != null)
s += next.toString();
return s;
}
public static void main(String[] args) {
Worm w = new Worm(6, 'a');
System.out.println("w = " + w);
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out "));
out.writeObject("Worm storage ");
out.writeObject(w);
out.close(); // Also flushes output
ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out "));
String s = (String) in.readObject();
Worm w2 = (Worm) in.readObject();
System.out.println(s + ", w2 = " + w2);
} catch (Exception e) {
e.printStackTrace();
}
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject("Worm storage ");
out.writeObject(w);
out.flush();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
String s = (String) in.readObject();
Worm w3 = (Worm) in.readObject();
System.out.println(s + ", w3 = " + w3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
相關推薦
Java Bean類實現Serializable介面的原因
為了儲存在記憶體中的各種物件的狀態(也就是例項變數,不是方法),並且可以把儲存的物件狀態再讀出來。雖然你可以用你自己的各種各樣的方法來儲存object states,但是Java為我們提供一種很好儲存物件狀態的機制,那就是序列化。 簡單來說序列化就是一種用來處理物件流的
hibernate pojo類 實現serializable介面
Object serialization的定義: Object serialization 允許你將實現了Serializable介面的物件轉換為位元組序列,這些位元組序列可以被完全儲存以備以後重新生成原來的物件。 其實實現不實現要根據你的需求而定,有些時候主要是為了能
GreenDao實體類實現Serializable介面報錯,自動生成 serialVersionUID
解決辦法: 在類中新增如下語句:static final long serialVersionUID = -15515456L; 選上以後,在你的class中:Alt+Enter就會提示自動建
【修真院java小課堂】什麼是序列化和反序列化,在RMI中是否要實現 SERIALIZABLE 介面, SERIALVERSIONUID的用處是什麼?
8.更多討論 1、serialVersionUID實際作用 假設本地資料庫中儲存了大量的user物件,後來由於需求,要修改User類中的屬性;如果不設定SerialVersionUID,根據屬性方法等自動生成,就會出現程式碼演示中的錯誤,造
Java - 一個類實現的多個介面,有相同簽名的default方法會怎麼辦
Java - 一個類實現的多個介面,有相同簽名的default方法會怎麼辦 public interface A { default void hello() { System.out.println("Hello from A"); }
java中,一個類實現某個介面,必須重寫介面中的所有方法嗎?拓展介面而不重寫相當於抽象類
不一定,關鍵要看子類是否是抽象類。 如果子類是非抽象類,則必須實現介面中的所有方法; 如果子類是抽象類,則可以不實現介面中的所有方法,因為抽象類中允許有抽象方法的存在! 1、抽象類定義 抽象類往往用來表徵對問題領域進行分析、設計中得出的抽象概念,是對一系列看上去不同,但
Java 物件序列化和反序列化 (實現 Serializable 介面)
序列化和反序列化的概念 把物件轉換為位元組序列的過程稱為物件的序列化。 把位元組序列恢復為物件的過程稱為物件的反序列化。 物件的序列化主要有兩種用途: 把物件的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中; 在網路上傳送物件的位元組序列。 JDK
java實現serializable介面的作用
一個物件序列化的介面,一個類只有實現了Serializable介面,它的物件才是可序列化的。因此如果要序列化某些類的物件,這些類就必須實現Serializable介面。而實際上,Serializable是一個空介面,沒有什麼具體內容,它的目的只是簡單的標識一個類的物件可以被
Java類實現某個介面後,是否需要實現介面中的所有方法?
1.結論 並不是所有的類實現介面後都必須實現介面的所有方法!2.特殊情況 當Java抽象類實現某個介面後沒必要實現所有的方法。3.注意點 當Java普通類實現介面後必須實現介面中的所有方法。4.原因為什麼抽象類不需要實現所有方法? 因為抽象類
Java中,一個類實現某個介面,必須重寫介面中的所有方法嗎
在這裡特別記錄一下,以防自己再次用錯~ “Java中,一個類實現了某介面,則必須實現該介面中的所有方法麼?”這句話其實是不準確的,因為我們還沒有考慮到抽象類。 抽象類實現某個介面,可以不實現所有介面的方法,可以由它的子類實現。而普通類即非抽象類則必須實現接口裡的全部方法。
java物件實現Serializable介面(整理)
Serializable 在工作中很少用到 之前也懂一些 今天特意整理一下 在還沒有深入瞭解serializable介面之前,像很多程式設計師一樣,以為一個物件實現serializable介
java中,一個類實現某個介面,必須重寫介面中的所有方法嗎?
java中,一個類實現某個介面,必須重寫介面中的所有方法嗎? 不一定,關鍵要看子類是否是抽象類。 如果子類是非抽象類,則必須實現介面中的所有方法; 如果子類是抽象類,則可以不實現介面中的所有方法,因為抽象類中允許有抽象方法的存在! 1、抽象類定義 抽象類往往用來表徵對問題領域進行分析
java 學習筆記--Comparator中為什麼其他類實現這個介面為什麼沒有複寫equals(Object obj)這個方法?
其實java存在好多隱藏的複寫。 大多數類好像未複寫的方法其實是繼承父類的而方法而隱藏複寫的。 而如下例: class HelloComparator implements Comparator {@Overridepublic int compare(Object o
為什麼實體類要實現serializable介面 序列化
最重要的兩個原因是: 1、將物件的狀態儲存在儲存媒體中以便可以在以後重新創建出完全相同的副本; 2、按值將物件從一個應用程式域傳送至另一個應用程式域。 實現serializable介面的作用是就是可以把物件存到位元組流,然後可以恢復。所以你想如果你的物件沒實現序列化怎
java類實現serializable好處及意義
一個物件序列化的介面,一個類只有實現了Serializable介面,它的物件才是可序列化的。因此如果要序列化某些類的物件,這些類就必須實現Serializable介面。而實際上,Serializable是一個空介面,沒有什麼具體內容,它的目的只是簡單的標識一個類的物件
Java定時任務:利用java Timer類實現定時執行任務的功能
lpad 虛擬 觀察 exce 就是 set ring 構造 trac 一、概述 在java中實現定時執行任務的功能,主要用到兩個類,Timer和TimerTask類。其中Timer是用來在一個後臺線程按指定的計劃來執行指定的任務。 TimerTask一個抽象類,它的子類代
Java原子類實現原理分析
upd hat 16px 檢查 () 過程 jvm api 處理 並發包中的原子類可以解決類似num++這樣的復合類操作的原子性問題,相比鎖機制,使用原子類更精巧輕量,性能開銷更小,下面就一起來分析下原子類的實現機理。 悲觀的解決方案(阻塞同步) 我們知道,num++看
32-多執行緒--概述+Thread類+多執行緒的建立方式(繼承Thread類+實現Runnable介面)+Runnable介面+執行緒的名稱+執行緒的狀態
一、概述 1、程序:對應的是一個應用程式在記憶體中的所屬空間。程序是不直接執行的,它只是在分配該應用程式的記憶體空間 注:如果一個程式在記憶體中開闢了空間,就代表它在執行。不執行要釋放空間 2、執行緒:程序中的一個負責程式執行的控制單元,也叫執行路徑。一個程序中可以有多個執行路徑,稱之為
大三筆記(摘抄--為什麼某些類需要Serializable介面)
因為最近埋頭苦幹於ssm框架,自然也就接觸到了將伺服器響應封裝為泛型類的知識點。 而這個類,需要實現Serializable。 什麼是Serializable介面?一個物件序列化的介面,一個類只有實現了Serializable介面,它的物件才能被序列化什麼是序列化?將物件的狀態資訊轉換為可以
java集合類實現簡單的學生資訊管理系統
package jihe; import java.util.Scanner; public class Student { private String sno; private String sname; private int grade; private int age; private S