日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java-Java I/O流解读之Object Serialization and Object Streams

發布時間:2025/3/21 java 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java-Java I/O流解读之Object Serialization and Object Streams 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  • 概述
  • 方法概述
  • 哪些類型的對象有資格進行序列化
  • ObjectInputStream ObjectOutputStream
  • javaioSerializable Externalizable Interfaces
    • javaioExternalizable Interface
      • 示例
  • 代碼

概述

數據流(DataInputStream和DataOutputStream)允許我們讀取和寫入原始數據(如int,double)和String,而不是單個字節。 對象流(ObjectInputStream和ObjectOutputStream)進一步讓我們讀取和寫入整個對象(如Date,ArrayList或任何自定義對象)。

對象序列化是在序列化比特流(bit-stream)中表示“對象的特定狀態”的過程,從而將比特流寫入外部設備(例如,磁盤文件或網絡)。 我們也可以重新構造bit-stream以恢復該對象的狀態。

對象序列化對于將對象的狀態保存到磁盤文件中以進行持久化是必需的,或者通過網絡將對象發送到Web服務,分布式對象應用程序和遠程方法調用(RMI)等應用程序。

在Java中,需要序列化的對象必須實現java.io.Serializable或java.io.Externalizable接口。 Serializable接口是一個沒有聲明的空接口(或標記接口)。 其目的只是聲明特定的對象是可序列化的。


方法概述

ObjectOutputStream類實現了ObjectOutput接口,該接口定義了將對象寫入輸出流的方法:

writeObject(Object)

將對象寫入底層存儲或流。 如果發生I / O錯誤,此方法將拋出IOException異常。

將對象寫入輸出流的過程稱為序列化。

ObjectOutput接口從DataOutput接口擴展,這意味著ObjectOutputStream繼承了寫入基本類型和字符串的所有行為,如DataOutputStream。

同樣,ObjectInputStream類實現了ObjectInput接口,該接口定義了一種從輸入流讀取對象的方法:

readObject()

讀取并返回一個對象。
如果找不到序列化對象的類,則此方法拋出ClassNotFoundException,如果發生I / O錯誤,則拋出IOException。

從輸入流重建對象的過程稱為反序列化。

ObjectInput接口從DataInput接口擴展,這意味著ObjectInputStream還具有讀取原始類型和字符串(如DataInputStream)的行為。

下面的類圖描述了對象流類和接口的API層次結構:


哪些類型的對象有資格進行序列化?

請注意,只有實現java.io.Serializable接口的類的對象才能被寫入輸出/輸入流并從其讀取。

Serializable是一個標記界面,它沒有定義任何方法。

只有標記為’serializable’的對象可以與ObjectOutputStream和ObjectInputStream一起使用。

Java中的大多數類(包括Date和原始包裝器Integer,Double,Long等)都實現了Serializable接口。 我們必須為我們的自定義類實現此接口。

如果嘗試編寫一個不可序列化的類的對象,將拋出一個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();}}}

默認情況下,原始類型和數組是可序列化的。

ObjectInputStream和ObjectOutputStream分別實現DataInput和DataOutput接口。我們可以使用readInt(),readDouble(),writeInt(),writeDouble()等方法來讀取和寫入原始類型。

transient & static
- 靜態字段不是序列化的,因為它屬于類而不是要序列化的特定實例。
- 為防止某些字段被序列化,請使用關鍵字transient標記它們。 這可以減少數據流量


java.io.Serializable & Externalizable Interfaces

當我們創建可能被序列化的類時,該類必須實現java.io.Serializable接口。 Serializable接口沒有聲明任何方法。 諸如Serializable之類的空接口稱為標記接口。 它們將實現類標識為具有某些屬性,而不需要這些類來實際實現任何方法。

大多數核心Java類都實現了Serializable,例如所有的包裝類,集合類和GUI類。 實際上,唯一沒有實現Serializable的核心Java類是不應該被序列化的。 原始數組或可序列化對象的數組本身是可序列化的。


java.io.Externalizable Interface

在序列化中,Java虛擬機完全負責寫入和讀取對象的過程。 這在大多數情況下是有用的,因為程序員不必關心序列化過程的底層細節。 但是,默認序列化不會保護敏感信息,例如密碼和憑據,或者程序員想要在序列化過程中要保護某些信息呢?

因此,Externalizable是為了讓程序員在序列化期間對對象的讀寫進行完全控制。

Serializable有一個Externalizable的子接口,如果要自定義類的序列化方式,可以使用它。 由于Externalizable擴展了Serializable,它也是一個Serializable,可以調用readObject()和writeObject()。

Externalizable聲明了兩種抽象方法:

void writeExternal(ObjectOutput out) throws IOException

The 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, ClassNotFoundException

The 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實現的接口,它們分別定義了writeObject()和readObject()方法。

當Externalizable的一個實例傳遞給ObjectOutputStream時,會繞過默認的序列化過程,會用實例的writeExternal()方法。

類似地,當ObjectInputStream讀取一個Exteranlizabled實例時,它使用readExternal()來重建該實例。

示例

假設我們有個User類,實現了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} }

強烈建議所有可序列化的類定義在上面的User類中聲明的serialVersionUID常量

public static final long serialVersionUID = 1234L;

這有助于反序列化過程在可序列化類超時更改時保持正確重新構建對象,并避免InvalidClassException。

下面來重寫writeExternal() 方法

由于writeExternal()方法使用ObjectOutput,我們可以使用它的方法將對象的狀態寫入基礎流,遵循以下規則:

  • 對于原始類型,使用DataOutput接口的writeXXX()方法,如writeBoolean(),writeByte(),writeInt(),writeLong()等)。

  • 對于對象類型(字符串,數組,自定義類),請使用writeObject()方法

@Overridepublic void writeExternal(ObjectOutput out) throws IOException {// implement your own code to write objects of this classout.writeInt(code);out.writeObject(name);// write empty password:out.writeObject("");out.writeObject(birthday);}

可以看到,我們序列化以下屬性: code, name, password and birthday。 為了安全起見,密碼設置為“”,并且沒有序列化socialSecurityNumber。 這給出了我們如何通過實現Externalizable接口來控制序列化過程的想法。


下面來重寫readExternal() 方法

由于readExternal()方法接受一個ObjectInput,我們可以使用它的方法從基礎流中讀取對象的狀態,遵循以下規則:

  • 對于原始類型,使用DataInput接口的readXXX()方法,如readBoolean(),readByte(),readInt(),readLong()等。

  • 對于對象類型(字符串,數組,您的自定義類),請使用readObject()方法。

@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {// implement your own code to read serialized objects of this classthis.code = in.readInt();this.name = (String) in.readObject();this.password = (String) in.readObject();this.birthday = (Date) in.readObject();}

我們可以看到,我們將以下屬性反序列化: code, name, password and birthday。 為了安全起見,socialSecurityNumber沒有被反序列化。 這給了我們通過實現Externalizable接口來控制反序列化過程的想法。

輸出:

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

總結

以上是生活随笔為你收集整理的Java-Java I/O流解读之Object Serialization and Object Streams的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。