java的對象流(序列化與反序列化)

1.序列化和反序列化

序  列 化:指把堆內存中的Java對象數據,通過某種方式把對象存儲到磁盤文件中或者傳遞給其他網絡的節點(在網絡上傳輸).
               我們把這個過程稱之爲序列化.
反序列化:把磁盤文件中的對象數據或者把網絡節點上的對象數據,恢復成Java對象的過程.
爲什麼要做序列化:
              1):在分佈式系統中,需要共享的數據的JavaBean對象,都得做序列化,此時需要把對象再網絡上傳輸,此時就得把對象數據轉換爲二進制形式.以後存儲在HttpSession中的對象,都應該實現序列化接口(只有實現序列化接口的類,才能做序列化操作).

              2):服務鈍化:如果服務發現某些對象好久都沒有活動了,此時服務器就會把這些內存中的對象,持久化在本地磁盤文件中(Java對象-->二進制文件).如果某些對象需要活動的時候,現在內存中去尋找,找到就使用,找不到再去磁盤文件中,反序列化我們得對象數據,恢復成Java對象.

需要做序列化的對象的類,必須實現序列化接口:java.io.Serializable接口(標誌接口[沒有抽象方法]).底層會判斷,如果當前對象是Serializable的實例,才允許做序列化.    boolean   ret = Java對象  instanceof  Serializable;

    在Java中大多數類都已經實現Serializable接口.

2.使用對象流來完成序列化和反序列化操作

     ObjectOutputStream:  通過writeObject方法做序列化操作的.
     ObjectInputStream:     通過readObject方法做反序列化操作的. 
   做反序列化操作必須存在對象的字節碼對象.

下面我們來對一個User對象做序列化和反序列化

首先創建一個User對象,就是我們平時使用的javaBean


public class User{
	private int id;
	private String nameString;
	private int age;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNameString() {
		return nameString;
	}
	public void setNameString(String nameString) {
		this.nameString = nameString;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public User(int id, String nameString, int age) {
		super();
		this.id = id;
		this.nameString = nameString;
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", nameString=" + nameString + ", age=" + age
				+ "]";
	}
}
測試:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class IODemo7 {
	public static void main(String[] args) throws Exception {
		writeObject();
	}
	
	//使用對象流對User對象進行序列化操作
	private static void writeObject() throws Exception {
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));
		out.writeObject(new User(1,"張三",18));
		out.close();
	}
}
此時會以下報錯,爲什麼?這是因爲我們自己定義的User類還沒有實現序列化接口


這時我們就應該修改User類,讓它實現Serializable接口

import java.io.Serializable;

public class User implements Serializable{
	private int id;
	private String nameString;
	private int age;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNameString() {
		return nameString;
	}
	public void setNameString(String nameString) {
		this.nameString = nameString;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public User(int id, String nameString, int age) {
		super();
		this.id = id;
		this.nameString = nameString;
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", nameString=" + nameString + ", age=" + age
				+ "]";
	}
}

這時運行就能成功將User對象寫入到文件中,然後我們打開肯定看不懂的,需要對象流輸入流來進行反序列化操作


下面我們進行反序列化

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class IODemo7 {
	public static void main(String[] args) throws Exception {
		writeObject();//序列化
		readObject();//反序列化
	}
	//使用對象流對User對象進行序列化操作
	private static void writeObject() throws Exception {
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));
		out.writeObject(new User(1,"張三",18));
		out.close();
	}
	//使用對象流對User對象進行反序列化操作
	private static void readObject() throws Exception {
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.txt"));
		User user = (User)in.readObject();
		System.out.println(user);//User [id=1, nameString=張三, age=18]
	}
}
打印結果:User [id=1, nameString=張三, age=18]

3.序列化的細節序列化的版本

    1):如果某些數據不需要做序列化,比如密碼,此時怎麼辦?
          理論上說,靜態的字段和瞬態的字段是不能做序列化操作的.

下面我們的User類添加一個password字段,然後在字段前面加上一個修飾符transient,看代碼

import java.io.Serializable;

public class User implements Serializable{
	private int id;
	transient private int password;//加上了transient修飾符
	private String nameString;
	private int age;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public void setPassword(int password) {
		this.password = password;
	}
	public int getPassword() {
		return password;
	}
	public String getNameString() {
		return nameString;
	}
	public void setNameString(String nameString) {
		this.nameString = nameString;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public User(int id, int password, String nameString, int age) {
		super();
		this.id = id;
		this.password = password;
		this.nameString = nameString;
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", password=" + password + ", nameString="
				+ nameString + ", age=" + age + "]";
	}
}
這時候的password就不會被序列化



    2):序列化的版本問題:
        反序列化Java對象時必須提供該對象的class文件,現在問題是,隨着項目的升級,系統的class文件也會升級(增加一個字段/刪除一個字段),如何保證兩個class文件的兼容性? Java通過serialVersionUID(序列化版本號)來判斷字節碼是否發生改變.如果不顯示定義serialVersionUID類變量,該類變量的值由JVM根據類相關信息計算,而修改後的類的計算方式和之前往往不同.從而造成了對象反序列化因爲版本不兼容而失敗的問題.







此時我們需要給我們的User加上一個序列化ID就可以了


那麼以後我們無論添加或者修改字段之後都不會在報錯,要保證序列化ID相同


總結:其實在開發中,我們只需要對我們自己定義的類加上序列化ID就行了,至於序列化和反序列化這個對象,框架會幫我們完成!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章