Java-Java I/O流解读之Object Serialization and Object Streams
- 概述
- 方法概述
- 哪些類型的對(duì)象有資格進(jìn)行序列化
- ObjectInputStream ObjectOutputStream
- javaioSerializable Externalizable Interfaces
- javaioExternalizable Interface
- 示例
- javaioExternalizable Interface
- 代碼
概述
數(shù)據(jù)流(DataInputStream和DataOutputStream)允許我們讀取和寫入原始數(shù)據(jù)(如int,double)和String,而不是單個(gè)字節(jié)。 對(duì)象流(ObjectInputStream和ObjectOutputStream)進(jìn)一步讓我們讀取和寫入整個(gè)對(duì)象(如Date,ArrayList或任何自定義對(duì)象)。
對(duì)象序列化是在序列化比特流(bit-stream)中表示“對(duì)象的特定狀態(tài)”的過程,從而將比特流寫入外部設(shè)備(例如,磁盤文件或網(wǎng)絡(luò))。 我們也可以重新構(gòu)造bit-stream以恢復(fù)該對(duì)象的狀態(tài)。
對(duì)象序列化對(duì)于將對(duì)象的狀態(tài)保存到磁盤文件中以進(jìn)行持久化是必需的,或者通過網(wǎng)絡(luò)將對(duì)象發(fā)送到Web服務(wù),分布式對(duì)象應(yīng)用程序和遠(yuǎn)程方法調(diào)用(RMI)等應(yīng)用程序。
在Java中,需要序列化的對(duì)象必須實(shí)現(xiàn)java.io.Serializable或java.io.Externalizable接口。 Serializable接口是一個(gè)沒有聲明的空接口(或標(biāo)記接口)。 其目的只是聲明特定的對(duì)象是可序列化的。
方法概述
ObjectOutputStream類實(shí)現(xiàn)了ObjectOutput接口,該接口定義了將對(duì)象寫入輸出流的方法:
writeObject(Object)將對(duì)象寫入底層存儲(chǔ)或流。 如果發(fā)生I / O錯(cuò)誤,此方法將拋出IOException異常。
將對(duì)象寫入輸出流的過程稱為序列化。
ObjectOutput接口從DataOutput接口擴(kuò)展,這意味著ObjectOutputStream繼承了寫入基本類型和字符串的所有行為,如DataOutputStream。
同樣,ObjectInputStream類實(shí)現(xiàn)了ObjectInput接口,該接口定義了一種從輸入流讀取對(duì)象的方法:
readObject()讀取并返回一個(gè)對(duì)象。
如果找不到序列化對(duì)象的類,則此方法拋出ClassNotFoundException,如果發(fā)生I / O錯(cuò)誤,則拋出IOException。
從輸入流重建對(duì)象的過程稱為反序列化。
ObjectInput接口從DataInput接口擴(kuò)展,這意味著ObjectInputStream還具有讀取原始類型和字符串(如DataInputStream)的行為。
下面的類圖描述了對(duì)象流類和接口的API層次結(jié)構(gòu):
哪些類型的對(duì)象有資格進(jìn)行序列化?
請(qǐng)注意,只有實(shí)現(xiàn)java.io.Serializable接口的類的對(duì)象才能被寫入輸出/輸入流并從其讀取。
Serializable是一個(gè)標(biāo)記界面,它沒有定義任何方法。
只有標(biāo)記為’serializable’的對(duì)象可以與ObjectOutputStream和ObjectInputStream一起使用。
Java中的大多數(shù)類(包括Date和原始包裝器Integer,Double,Long等)都實(shí)現(xiàn)了Serializable接口。 我們必須為我們的自定義類實(shí)現(xiàn)此接口。
如果嘗試編寫一個(gè)不可序列化的類的對(duì)象,將拋出一個(gè)java.io.NotSerializableException.
ObjectInputStream & ObjectOutputStream
StudentRecordWriter
package com.xgj.master.java.io.fileDemo.byteStreams.objectStreams;import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List;import org.junit.Test;/*** * * @ClassName: StudentRecordWriter* * @Description: This class will uses the ObjectOutputStream class to write a* list of Students object to a file on disk* * @author: Mr.Yang* * @date: 2017年9月7日 下午5:25:28*/ public class StudentRecordWriter {@Testpublic void writeStudent2DiskFile() {DateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");// JDK 7中的寫法try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(new File("D:\\StudentRecord.txt"))))) {List<Student> studentList = new ArrayList<>();studentList.add(new Student("Xiao", dateFormat.parse("1993-02-15"), false, 23, 80.5f));studentList.add(new Student("Gong", dateFormat.parse("1994-10-03"), true, 22, 95.0f));studentList.add(new Student("Jiang", dateFormat.parse("1995-08-22"), false, 21, 79.8f));for (Student student : studentList) {oos.writeObject(student);}} catch (IOException | ParseException ex) {ex.printStackTrace();}} }StudentRecordReader
package com.xgj.master.java.io.fileDemo.byteStreams.objectStreams;import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.text.DateFormat; import java.text.SimpleDateFormat;import org.junit.Test;/*** * * @ClassName: StudentRecordReader* * @Description: StudentRecordReader uses the ObjectInputStream class to read* objects from a file on disk* * @author: Mr.Yang* * @date: 2017年9月7日 下午5:31:50*/ public class StudentRecordReader {@Testpublic void readStudentFromDiskFile() {DateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");try (ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File("D:\\StudentRecord.txt"))))) {while (true) {Student student = (Student) ois.readObject();System.out.print(student.getName() + "\t");System.out.print(dateFormat.format(student.getBirthday()) + "\t");System.out.print(student.getGender() + "\t");System.out.print(student.getAge() + "\t");System.out.println(student.getGrade());}} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}}默認(rèn)情況下,原始類型和數(shù)組是可序列化的。
ObjectInputStream和ObjectOutputStream分別實(shí)現(xiàn)DataInput和DataOutput接口。我們可以使用readInt(),readDouble(),writeInt(),writeDouble()等方法來(lái)讀取和寫入原始類型。
transient & static
- 靜態(tài)字段不是序列化的,因?yàn)樗鼘儆陬惗皇且蛄谢奶囟▽?shí)例。
- 為防止某些字段被序列化,請(qǐng)使用關(guān)鍵字transient標(biāo)記它們。 這可以減少數(shù)據(jù)流量
。
java.io.Serializable & Externalizable Interfaces
當(dāng)我們創(chuàng)建可能被序列化的類時(shí),該類必須實(shí)現(xiàn)java.io.Serializable接口。 Serializable接口沒有聲明任何方法。 諸如Serializable之類的空接口稱為標(biāo)記接口。 它們將實(shí)現(xiàn)類標(biāo)識(shí)為具有某些屬性,而不需要這些類來(lái)實(shí)際實(shí)現(xiàn)任何方法。
大多數(shù)核心Java類都實(shí)現(xiàn)了Serializable,例如所有的包裝類,集合類和GUI類。 實(shí)際上,唯一沒有實(shí)現(xiàn)Serializable的核心Java類是不應(yīng)該被序列化的。 原始數(shù)組或可序列化對(duì)象的數(shù)組本身是可序列化的。
java.io.Externalizable Interface
在序列化中,Java虛擬機(jī)完全負(fù)責(zé)寫入和讀取對(duì)象的過程。 這在大多數(shù)情況下是有用的,因?yàn)槌绦騿T不必關(guān)心序列化過程的底層細(xì)節(jié)。 但是,默認(rèn)序列化不會(huì)保護(hù)敏感信息,例如密碼和憑據(jù),或者程序員想要在序列化過程中要保護(hù)某些信息呢?
因此,Externalizable是為了讓程序員在序列化期間對(duì)對(duì)象的讀寫進(jìn)行完全控制。
Serializable有一個(gè)Externalizable的子接口,如果要自定義類的序列化方式,可以使用它。 由于Externalizable擴(kuò)展了Serializable,它也是一個(gè)Serializable,可以調(diào)用readObject()和writeObject()。
Externalizable聲明了兩種抽象方法:
void writeExternal(ObjectOutput out) throws IOExceptionThe object implements this method to save its contents by calling the methods of DataOutput for its primitive values or calling the writeObject method of ObjectOutput for objects, strings, and arrays.
void readExternal(ObjectInput in) throws IOException, ClassNotFoundExceptionThe object implements this method to restore its contents by calling the methods of DataInput for primitive types and readObject for objects, strings and arrays.
ObjectOutput和ObjectInput是由ObjectOutputStream和ObjectInputStream實(shí)現(xiàn)的接口,它們分別定義了writeObject()和readObject()方法。
當(dāng)Externalizable的一個(gè)實(shí)例傳遞給ObjectOutputStream時(shí),會(huì)繞過默認(rèn)的序列化過程,會(huì)用實(shí)例的writeExternal()方法。
類似地,當(dāng)ObjectInputStream讀取一個(gè)Exteranlizabled實(shí)例時(shí),它使用readExternal()來(lái)重建該實(shí)例。
示例
假設(shè)我們有個(gè)User類,實(shí)現(xiàn)了externalization 接口
public class User implements Externalizable {public static final long serialVersionUID = 1234L;// attributesprivate int code;private String name;private String password;private Date birthday;private int socialSecurityNumber;public User() {}// methods (getters and setters)public int getCode() {return this.code;}public void setCode(int code) {this.code = code;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public String getPassword() {return this.password;}public void setPassword(String password) {this.password = password;}public void setBirthday(Date birthday) {this.birthday = birthday;}public Date getBirthday() {return this.birthday;}public void setSocialSecurityNumber(int ssn) {this.socialSecurityNumber = ssn;}public int getSocialSecurityNumber() {return this.socialSecurityNumber;}// externalization methods:public void writeExternal(ObjectOutput out) {// implement your own code to write objects of this class}public void readExternal(ObjectInput in) {// implement your own code to read serialized objects of this class} }強(qiáng)烈建議所有可序列化的類定義在上面的User類中聲明的serialVersionUID常量
public static final long serialVersionUID = 1234L;這有助于反序列化過程在可序列化類超時(shí)更改時(shí)保持正確重新構(gòu)建對(duì)象,并避免InvalidClassException。
下面來(lái)重寫writeExternal() 方法
由于writeExternal()方法使用ObjectOutput,我們可以使用它的方法將對(duì)象的狀態(tài)寫入基礎(chǔ)流,遵循以下規(guī)則:
對(duì)于原始類型,使用DataOutput接口的writeXXX()方法,如writeBoolean(),writeByte(),writeInt(),writeLong()等)。
對(duì)于對(duì)象類型(字符串,數(shù)組,自定義類),請(qǐng)使用writeObject()方法
可以看到,我們序列化以下屬性: code, name, password and birthday。 為了安全起見,密碼設(shè)置為“”,并且沒有序列化socialSecurityNumber。 這給出了我們?nèi)绾瓮ㄟ^實(shí)現(xiàn)Externalizable接口來(lái)控制序列化過程的想法。
下面來(lái)重寫readExternal() 方法
由于readExternal()方法接受一個(gè)ObjectInput,我們可以使用它的方法從基礎(chǔ)流中讀取對(duì)象的狀態(tài),遵循以下規(guī)則:
對(duì)于原始類型,使用DataInput接口的readXXX()方法,如readBoolean(),readByte(),readInt(),readLong()等。
對(duì)于對(duì)象類型(字符串,數(shù)組,您的自定義類),請(qǐng)使用readObject()方法。
我們可以看到,我們將以下屬性反序列化: code, name, password and birthday。 為了安全起見,socialSecurityNumber沒有被反序列化。 這給了我們通過實(shí)現(xiàn)Externalizable接口來(lái)控制反序列化過程的想法。
輸出:
User's details before serialization: Code: 123 Name: Tom Birthday: Fri Sep 08 14:02:30 BOT 2017 Password: secret123 socialSecurityNumber: 1234567890 Serialization done=============User's details afeter de-serialization: Code: 123 Name: Tom Birthday: Fri Sep 08 14:02:30 BOT 2017 Password: socialSecurityNumber: 0代碼
代碼已托管到Github—> https://github.com/yangshangwei/JavaMaster
總結(jié)
以上是生活随笔為你收集整理的Java-Java I/O流解读之Object Serialization and Object Streams的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-Java I/O流解读之jav
- 下一篇: Java-Java I/O 字节流之Bu