1. 程式人生 > >Android中序列化與反序列化

Android中序列化與反序列化

這幾天在看到設計模式時,看到有關於序列化的知識,發現自己之前這塊知識很欠缺,所以這花了兩天仔細研究了一下,感覺這個東西還是很有趣的(當然也很有用-。+),今天給大家帶來這篇文章,希望對大家有幫助。

序列化概念和應用

首先我們需要知道序列化是個什麼意思。

  • 序列化:將物件轉換為可傳輸的二進位制流的過程。
  • 反序列化:把位元組序列恢復成物件的過程。

我舉個栗子:我們都進行過檔案操作吧。我們在檔案中寫數字,或者寫漢字,再或者寫英文,這些東西都是可以直接轉為二進位制位元組的,我們把需要的資料儲存在File中,實現了資料儲存到裝置中,實現了資料持久化。

但是現在我有一個物件需要暫時儲存到檔案中,這個跟剛才的基本型別不一樣了哦。所以說怎麼辦?

還有這個情況:我們在進行網路傳輸,傳輸賬號、密碼等,這些都是基本資料。那我如果想傳輸一個物件過去呢?到了對方電腦上之後,鬼知道你這是個什麼東西。。。。

根據這兩種案例,相信大家已經對序列化的應用有所瞭解了,主要可以分為以下幾類:

  1. 儲存到檔案或本地資料庫(如SQLite)這類持久化資料操作。
  2. 通過網路請求傳輸物件。
  3. 記憶體中資料呼叫(Intent,下面會多次用到)。

一句話概括,就是物件進行儲存和傳遞的一種方法:轉化為二進位制流。

而進行序列化主要有兩個重要的類:Serializable類和Parcelable類。我們分別來看一下。

Serializable類

首先說一下這個類,相信在Java中大家可能已經見到過這個類了。這裡簡單介紹一下:

public interface Serializable {
}

Serializable是一個標記介面,從上面的類中也可以看出來,他是一個空的介面。

我們傳輸時候,各種各樣的類,我們需要有一個類來對他們進行統一。但是Java中是單根繼承,而介面恰好彌補了這個地方。我們可以將實現的藉口看做父類,這樣通過Serializable類就實現了多種類的統一,同時為類打上了標籤,可以通過instanceof Serializable判斷是否能序列化(個人理解)。

而它使用起來也很簡單,只將需要序列化的類實現Serializable類就好了,其他的我們完全不用操作。

Serializable在Java中就有了,我們先來看一下在Java中它是如何使用:

Java中使用Serializable

盜取了一張大神的圖,我們在學習IO流中,有兩個類是用於讀寫物件的——ObjectInputStream和ObjectOutputStream。

ObjectOutputStream負責將記憶體中的物件寫入儲存中。ObjectInputStream負責從本地儲存中讀取存入的物件。而是用這兩個類,那麼傳入的這個類必須是序列化的。我們看一下程式碼就知道了:

import java.io.Serializable;

public class StudentBean implements Serializable{

	private int age;
	private String name;
	private int id;
	
	public StudentBean(int age, String name, int id) {
		super();
		this.age = age;
		this.name = name;
		this.id = id;
	}
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@Override
	public String toString() {
		return "StudentBean [age=" + age + ", name=" + name + ", id=" + id + "]";
	}
	
}
這是一個Bean類,實現了get和set方法,構造方法和重寫了toString方法。
public static ObjectOutputStream serilizeData(StudentBean studentBean) {
	ObjectOutputStream objectOutputStream = null ;
		
	try {
		objectOutputStream = new ObjectOutputStream(
			new FileOutputStream(
				      new File("/Users/jibai/Desktop/" + StudentBean.class.getSimpleName() + ".txt")
				)
			);
			
		objectOutputStream.writeObject(studentBean);
			
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
		
	return objectOutputStream;
}

這是我們將序列化的Bean類通過類輸入流寫入檔案的方法。

public static StudentBean reverseSerilizeData(String path) {
		
	StudentBean bean = null;
		
	try {
		ObjectInputStream objectInputStream = new ObjectInputStream(
				new FileInputStream(
						new File(path)
						)
				);
			
		bean = (StudentBean) objectInputStream.readObject();
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
		
	return bean;
}

這個是將本地儲存物件的檔案讀取出來的方法。

最後看一下main方法:

public static void main(String[] args) {
	// TODO Auto-generated method stub
		
	serilizeData(new StudentBean(18, "tom", 01));
	StudentBean bean = reverseSerilizeData("/Users/jibai/Desktop/" + StudentBean.class.getSimpleName() + ".txt");
	System.out.println(bean.toString());
		
}

首先把一個StudentBean的物件儲存到檔案中,然後再將這個檔案中的物件讀取出來,我們看一下結果:

桌面確實有StudentBean這個檔案,而且控制檯也成功得把這個檔案中的物件讀出來了。

如果我們沒有讓StudentBean類實現Serializable介面,會怎麼樣。

java.io.NotSerializableException: StudentBean
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at MyProject.serilizeData(MyProject.java:30)
	at MyProject.main(MyProject.java:14)
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: StudentBean
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1575)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
	at MyProject.reverseSerilizeData(MyProject.java:51)
	at MyProject.main(MyProject.java:15)
Caused by: java.io.NotSerializableException: StudentBean
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at MyProject.serilizeData(MyProject.java:30)
	at MyProject.main(MyProject.java:14)
Exception in thread "main" java.lang.NullPointerException
	at MyProject.main(MyProject.java:16)

第一行很明顯,丟擲了一個異常,說StudentBean類沒有序列化。

Android下Serializable

關於這個實在沒什麼特別需要講的,因為使用也只是實現這個介面就好了。等會在Parcelable中會順便提一下。

小細節

其實關於Serializable還有一個知識點需要了解:我們在實現Serializable時,類會有一個序列化的值,這個值預設的情況下會隨我們Bean類中屬性成員變化而變化。

如果在eclipse中沒有定義這個值,那麼類名會有一個警告:

public class StudentBean implements Serializable{

	/**
	 * 
	 */
	private int age;
	private String name;
	private int id;

添加了UID之後:

private static final long serialVersionUID = 630907180238371889L;
	/**
	 * 
	 */
	private int age;
	private String name;
	private int id;

如果對類成員屬性進行了修改,那麼再次讀取這個物件時,會因為UID不同而丟擲異常:

java.io.InvalidClassException: StudentBean; local class incompatible: stream classdesc serialVersionUID = -8088515121892865631, local class serialVersionUID = 630907180238371889
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1883)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1749)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2040)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
	at MyProject.reverseSerilizeData(MyProject.java:51)
	at MyProject.main(MyProject.java:15)
Exception in thread "main" java.lang.NullPointerException
	at MyProject.main(MyProject.java:16)

所以我們在實現Serializable介面同時,在類中要宣告UID。

Parcelable類

Parcelable類相比於Serializable,要複雜多了,Parcelable類是Android推出的高效的序列化介面(據說和Serializable效能對比,是其10倍速度,真的假的我不知道)。

先看一下這個介面的結構:

public class RectBean implements Parcelable {


    public RectBean() {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }

    protected RectBean(Parcel in) {
    }

    public static final Creator<RectBean> CREATOR = new Creator<RectBean>() {
        @Override
        public RectBean createFromParcel(Parcel source) {
            return new RectBean(source);
        }

        @Override
        public RectBean[] newArray(int size) {
            return new RectBean[size];
        }
    };
}

序列化方法和步驟

這是我們實現Parcelable介面的一系列方法。主要可以分為四步:

  1. 建立私有化構造方法(或者protected)
  2. 重寫describeContents方法。
  3. 重寫writeToParcel方法,這個方法是我們將物件序列化的方法。
  4. 實現Creator類,並實現createFromParcel方法和newArray方法,newArray方法不是很重要,主要看createFromParcel方法,這個方法是我們反序列化得到物件的方法。
這是幾個重要的方法:

方法

作用

describeContents

返回當前物件的描述,01(大多數情況都返回0

writeToParcelParcel outint flag

將當前物件寫入序列化結構,flag標識有011時標識當前物件作為返回值返回(大多數情況返回0

createFromParcelParcel in

從序列化後的物件中建立原始物件

Parcel類

在這裡要介紹一下里面出現很多的一個類——Parcel。這個類翻譯過來就是打包的意思,而我們序列化後的物件全都儲存在了這個裡面。

其實Parcelable類只是一個外殼,而真正實現了序列化的,是Parcel這個類,它裡面有大量的方法,對各種資料進行序列化(下面我們會用到)。

以上是Parcel中的一部分方法(太多太多了)。

關於Parcel我們現在不用瞭解太多,我們只需要知道是它實現序列化和反序列化功能就夠了。

不同型別屬性序列化操作

下面我們向RectBean中新增屬性,而這裡面屬性可以分為四種:

  • 除boolean之外的基本型別(int、float、char、)
  • boolean型別
  • 物件型別
  • 集合型別(List、。。。)

首先說一下,我們在通過Parcel類進行序列化和反序列化操作時,對應的不同型別的屬性,需要呼叫不同的方法,在上面我們也看到了,Parcel眾多型別的方法,就是為了對應不同型別的屬性。

但是看了這麼多方法,發現沒有關於boolean型別的方法。所以才把boolean單拿出來。

接下來我們一個一個的看:

1.除boolean外的基本型別

public class RectBean implements Parcelable {

    private float x,y;
    
    public RectBean() {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(this.x);
        dest.writeFloat(this.y);
    }

    protected RectBean(Parcel in) {
        this.x = in.readFloat();
        this.y = in.readFloat();
    }

    public static final Creator<RectBean> CREATOR = new Creator<RectBean>() {
        @Override
        public RectBean createFromParcel(Parcel source) {
            return new RectBean(source);
        }

        @Override
        public RectBean[] newArray(int size) {
            return new RectBean[size];
        }
    };
}

我給RectBean類添加了兩個float的屬性:x、y。看一下哪些方法需要修改。

在writeToParcel中我們呼叫了Parcel的readFloat方法,將兩個屬性序列化,儲存到Parcel中。

然後在createFromParcel中,又通過readFloat中,將序列化的物件還原成原物件。

很簡單,沒有難度對吧,只是兩個方法而已。

2.boolean型別

由於沒有boolean型別儲存,我們可以用byte或者int型別來進行儲存:

public class RectBean implements Parcelable {

    private float x,y;
    private boolean isVisible;

    public RectBean() {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(this.x);
        dest.writeFloat(this.y);
        dest.writeByte(this.isVisible ? (byte) 1 : (byte) 0);
    }

    protected RectBean(Parcel in) {
        this.x = in.readFloat();
        this.y = in.readFloat();
        this.isVisible = in.readByte() != 0;
    }

    public static final Creator<RectBean> CREATOR = new Creator<RectBean>() {
        @Override
        public RectBean createFromParcel(Parcel source) {
            return new RectBean(source);
        }

        @Override
        public RectBean[] newArray(int size) {
            return new RectBean[size];
        }
    };
}

通過一個條件運算子就解決了,讓true值為1,false值為0。

3.屬性為物件型別。

如果說上面的兩中都是基本型別,那麼這個是一個物件型別,怎麼解決?

首先第一步,我們新增的物件屬性對應的類,一定也要被序列化。這個應該很容易理解:

public class Point implements Parcelable {
    protected Point(Parcel in) {
    }

    public static final Creator<Point> CREATOR = new Creator<Point>() {
        @Override
        public Point createFromParcel(Parcel in) {
            return new Point(in);
        }

        @Override
        public Point[] newArray(int size) {
            return new Point[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }
}

建立一個Point類,並讓它實現Parcelable介面。

public class RectBean implements Parcelable {

    private float x,y;
    private boolean isVisible;
    private Point point;

    public RectBean() {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(this.x);
        dest.writeFloat(this.y);
        dest.writeByte(this.isVisible ? (byte) 1 : (byte) 0);
        dest.writeParcelable(this.point, flags);
    }

    protected RectBean(Parcel in) {
        this.x = in.readFloat();
        this.y = in.readFloat();
        this.isVisible = in.readByte() != 0;
        this.point = in.readParcelable(Point.class.getClassLoader());
    }

    public static final Creator<RectBean> CREATOR = new Creator<RectBean>() {
        @Override
        public RectBean createFromParcel(Parcel source) {
            return new RectBean(source);
        }

        @Override
        public RectBean[] newArray(int size) {
            return new RectBean[size];
        }
    };
}

然後在RectBean中新增Point類的屬性。這次呼叫的是Parcel的readParcelable方法和writeParcelable方法。但是在反序列化時候,需要用到屬性類的類載入器。

this.point = in.readParcelable(Point.class.getClassLoader());

4.集合型別的屬性。

集合型別的屬性切記,一定要初始化!!!!!

public class RectBean implements Parcelable {

    private float x,y;
    private boolean isVisible;
    private Point point;
    private List<Point> points  = new ArrayList<>();

    public RectBean() {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(this.x);
        dest.writeFloat(this.y);
        dest.writeByte(this.isVisible ? (byte) 1 : (byte) 0);
        dest.writeParcelable(this.point, flags);
        dest.writeTypedList(this.points);
    }

    protected RectBean(Parcel in) {
        this.x = in.readFloat();
        this.y = in.readFloat();
        this.isVisible = in.readByte() != 0;
        this.point = in.readParcelable(Point.class.getClassLoader());
        this.points = in.createTypedArrayList(Point.CREATOR);
    }

    public static final Creator<RectBean> CREATOR = new Creator<RectBean>() {
        @Override
        public RectBean createFromParcel(Parcel source) {
            return new RectBean(source);
        }

        @Override
        public RectBean[] newArray(int size) {
            return new RectBean[size];
        }
    };
}

這裡需要說明一下:

我們如果說List存放的是String,那麼在序列化和反序列化對應的方法就是writeStringList和createStringArrayList:

dest.writeStringList(this.points);
.....
this.points = in.createStringArrayList();

如果是系統中自帶的類,那麼會呼叫writeList和readList方法,而在readList中也需要系統類的類載入器:

dest.writeList(this.points);
...
in.readList(this.points, Thread.class.getClassLoader());

而如果是自定義的類,那麼會呼叫writeTypedList和createTypedArrayList,在createTypedArrayList中將自定義類的CREATOR傳入。(說明該自定義類也需要序列化

dest.writeTypedList(this.points);
...
this.points = in.createTypedArrayList(Point.CREATOR);

以上便是Parcelable的主要使用方法了。下面我們來對兩種序列化做一個總結:

Serializable和Parcelable總結

  1. 兩者都是實現序列化得介面,都可以用Intent傳遞資料。
  2. Serializable使用時會產生大量的臨時變數,進行IO操作頻繁,消耗比較大,但是實現方式簡單。
  3. Parcelable是Android提供輕量級方法,效率高,但是實現複雜。
  4. 一般在記憶體中序列畫傳遞時選用Parcelable。在裝置或網路中傳遞選用Serializable。
  5. 無論是Serializable還是Parcelable,兩種內屬性只要有物件,那麼對應物件的類一定也要實現序列化。

以上便是今天的全部內容,希望對大家有所幫助,喜歡的朋友希望多多支援一下,有不同意見或者有錯誤的地方希望大家留言評論,謝謝大家支援!!

相關推薦

Android序列序列

這幾天在看到設計模式時,看到有關於序列化的知識,發現自己之前這塊知識很欠缺,所以這花了兩天仔細研究了一下,感覺這個東西還是很有趣的(當然也很有用-。+),今天給大家帶來這篇文章,希望對大家有幫助。序列化概念和應用首先我們需要知道序列化是個什麼意思。序列化:將物件轉換為可傳輸的

asp.net mvc如何處理字符串對象之間的序列序列(一)

osi strong 類結構 plain pbo edate inf esc arp 前臺我們一般用ajax來發送數據到後端進行處理,如果json數據結構和後臺的實體類結構一致,就直接可以反序列化為指定的對象進行操作,非常方便。 前端發送的json數據結構: 後端實體結

python序列序列

tmp 數據類型 load 一個 port style pick 序列 spa 之前,在學習python時,一直弄不明白pickle和json模塊的序列化和反序例化之間的區別和用法,最近閑來有時間,重新研究了這兩個模塊,也算是基本搞明白他們之中的區別了。 用於序列化的兩個模

java序列序列的問題

java序列化是將java物件轉換為位元組序列的過程,變成計算機能夠儲存的二進位制序列       反序列化是將位元組序列恢復成java物件的過程 1.當兩個Java程序進行通訊時,能否實現程序間的物件傳送呢?答案是可以的。如何做到呢?這就需要Java序列化與反

c# 通過json.net的JsonConverter進行自定義序列序列

iter 希望 生成 ade json.net .json implement else col   相信大家在工作中會經常遇見對json進行序列化與反序列化吧,但通常的序列化與反序列化中的json結構與c#中的類模型結構是相對應的,我們是否可以在序列化一個對象時候,讓我們

java序列序列

序列化與反序列化 一 、什麼是序列化與反序列化 把物件轉換為位元組序列的過程稱為物件的序列化。 把位元組序列恢復為物件的過程稱為物件的反序列化 一個物件只要實現了Serilizable介面,這個物件就可以被序列化,java的這種序列化模式為開發者提供了很多便利

分散式系統序列序列

1.定義以及相關概念 作者 劉丁 釋出於 2015年5月7日 | 3 討論 分享到: 微博 微信 Facebook Twitter 有道雲筆記 郵件分享 稍後閱讀我的閱讀清單 簡介 文章作者服務於美團推薦與個性化組,該組致力於為美團使用者提供每天bi

java序列序列

       把物件轉換為位元組序列的過程稱為物件的序列化。   把位元組序列恢復為物件的過程稱為物件的反序列化。   物件的序列化主要有兩種用途:   1) 把物件的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中;   2) 在網路上傳送物件的位元組序列

Java的基礎----序列序列的作用

Java提供兩種物件持久化的方式,分別序列化和外部序列化。 1)序列化(Serialization):         在分散式環境下,無論是何種資料,都會以二進位制序列的形式在網路上傳輸。序列化是一種將物件以一連串的位元組描述的過程,用於解決在對物件流進行讀寫操作時

Android序列序列

1. 什麼是java序列化,如何實現java序列化? 我們有時候將一個java物件變成位元組流的形式傳出去或者從一個位元組流中恢復成一個java物件,例如,要將java物件儲存到硬碟或者傳送給網路上的其他計算機,這個過程我們可以自己寫程式碼去把一個java物件

Java資料通訊使用Google Protobuf實現序列序列

一、.什麼是protocol buffer        ProtocolBuffer是用於結構化資料序列化的靈活、高效、自動的方法,有如XML,不過它更小、更快、也更簡單。你可以定義自己的資料結構,然後使用程式碼生成器生成的程式碼來讀寫這個資料結構。你甚至可以在無需重新

java序列序列transient和static成員剖析

在我的上一篇博文中講解了一些基本的關於java序列化與反序列化的問題,現在我們一起來對物件中宣告為transient和static的變數進行解析。 1:類中宣告為transient變數 一旦類中某個變數宣告為transient,則會告訴JVM,你不用幫我序列化該變數,我自己

有關java物件序列序列的亂碼問題

在一開始學習java流操作中的有關物件序列化與非序列化的時候,很多初學者會糾結於寫出的檔案出現亂碼的問題。 以下是有關序列化與反序列化的相關程式碼。 第一步:先建立相關的物件。package day02; import java.io.Serializable; impo

JSON的序列序列及其在前後臺互動的應用

1、什麼是JSON   JSON是JavaScript Object Notation的縮寫,它是一種資料交換格式。   在JSON出現之前,大家一直用XML來傳遞資料。因為XML是一種純文字格式

Protobuf-net在Unity序列序列

本篇中我們只講解如何在Unity中對Protobuf-net進行序列化(Serialize)與反序列化(Deserialize),關於Unity的socket網路通訊部分我們後續開篇。 首先去Protobuf-net的Google下載點下載protobuf-net類庫: 這裡用的是目前最新的protobuf

淺談C#序列序列

今天我利用這篇文章給大家講解一下C#中的序列化與反序列化。這兩個概念我們再開發中經常用到,但是我們絕大部分只用到了其中的一部分,剩下的部分很多開發人員並不清楚,甚至可以說是不知道。因此我希望通過這篇文章能讓大家對序列化和反序列化的知識有更進一步的掌握。廢話不多說,開始進入正題。 一、什麼是序列化/反序列化 &

Java 序列序列

一、 序列化和反序列化概念 Serialization(序列化)是一種將物件以一連串的位元組描述的過程;反序列化deserialization是一種將這些位元組重建成一個物件的過程。將程式中的物件,放入檔案中儲存就是序列化,將檔案中的位元組碼重新轉成物件就是反序列化。 二、 序列化和反序列化的必要性 當兩個程

Java核心類庫-IO-對象流(實現序列序列

.get throws 反序 code row cts new java cep 使用對象流來完成序列化和反序列化操作:   ObjectOutputStream:通過writeObject()方法做序列化操作的   ObjectInputStream:通過readObje

Java IO-5 序列序列

str ride log getname file urn turn objects transient 建一個Person類 1 package demo05; 2 3 import java.io.Serializable; 4 5 public cla

契約類相關的序列序列

pub ima cti 相關 ria 數據 num spa set 契約類指繼承了:DataContract的類。契約類常在WCF,webService等各種服務中作為傳輸數據使用。 凡是契約類或者繼承了契約類的類,如果想要屬性參與序列化與反序列化,需要在屬性上加上標記:D