自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 高級特性之使用反射實(shí)現(xiàn)萬能序列化

開發(fā) 后端
很多時(shí)候我們需要將一個(gè)類的實(shí)例變成二進(jìn)制數(shù)據(jù)存儲(chǔ)或是通過網(wǎng)絡(luò)發(fā)送,這個(gè)過程叫序列化。

[[437080]]

很多時(shí)候我們需要將一個(gè)類的實(shí)例變成二進(jìn)制數(shù)據(jù)存儲(chǔ)或是通過網(wǎng)絡(luò)發(fā)送,這個(gè)過程叫序列化。如果將二進(jìn)制數(shù)據(jù)解析成位于內(nèi)存中的類實(shí)例或是相關(guān)數(shù)據(jù)結(jié)構(gòu),那叫反序列化。所有的序列化算法都遵循一定的套路,例如:

  1. class A { 
  2.  
  3. public int a = 1; 
  4.  
  5. public int b = 2; 
  6.  
  7. protected B b = new B(); 
  8.  
  9. private float c = 3.0; 
  10.  

如果要序列化類A的實(shí)例,那么通常需要將變量a,b的數(shù)值對應(yīng)的二進(jìn)制數(shù)寫入,然后獲得類B實(shí)例序列化后的二進(jìn)制數(shù)據(jù),最后將變量c的數(shù)值的二進(jìn)制數(shù)據(jù),這里可以體會(huì)到,序列化其實(shí)有一種遞歸的性質(zhì),在序列化過程中如果遇到的是基礎(chǔ)類型,那么可以直接獲取其對應(yīng)的二進(jìn)制數(shù)據(jù),如果遇到類實(shí)例,那么需要先序列化它,取得對應(yīng)二進(jìn)制數(shù)據(jù)。

而序列化過程中需要你了解對應(yīng)類的定義,但如果我們不知道要序列化的對象,例如我們看不到類A的定義,我們只拿到了A對應(yīng)的一個(gè)實(shí)例對象,那此時(shí)怎么序列化呢。這就需要用到j(luò)ava語言的反射特性,java編譯器在編譯類A時(shí),不僅僅將它為它的各個(gè)字段分配了內(nèi)存,而且還為類A的相關(guān)信息進(jìn)行了設(shè)置和存儲(chǔ),例如A里面有多少字段,字段的類型是int, float, stirng,還是特定類對象,這些信息都一并設(shè)置并存儲(chǔ)了起來,只要我們使用java反射提供的API就能獲得這些信息,從而就能對任意類實(shí)現(xiàn)序列化。

因此序列化的萬能套路是:

1,獲得要序列化的類實(shí)例;

2,獲得類中各個(gè)字段的屬性,類型等相關(guān)信息。

3,如果字段屬于基礎(chǔ)數(shù)據(jù),那么獲得其數(shù)值的二進(jìn)制數(shù)據(jù)。

4,如果對應(yīng)字段是一個(gè)類實(shí)例,那么先遞歸的序列化該實(shí)例

根據(jù)以上步驟,當(dāng)我們需要序列化任意一個(gè)類實(shí)例時(shí),首先通過getClass獲得其對應(yīng)的Class類實(shí)例,然后調(diào)用getDeclaredFields()接口獲得該實(shí)例所有的字段,其中包括public,protected,private,或者調(diào)用getFields()獲得類實(shí)例聲明或繼承的公有字段。在序列化中,我們不能忘了序列化當(dāng)前類實(shí)例的父類,因此可以調(diào)用getSuperClass()來獲得當(dāng)前實(shí)例的父類,這個(gè)過程會(huì)不斷進(jìn)行直到抵達(dá)根類為止。

每個(gè)字段都會(huì)對應(yīng)一個(gè)元類叫Field,通過該類相關(guān)接口能獲得字段的值。獲取字段的數(shù)據(jù)首先需要確定字段的類型,如果是Boolean類型,那么可以調(diào)用Field類的getBoolean接口獲得數(shù)據(jù),如果是int類型,那么可以通過getInteger()接口獲得數(shù)據(jù),如果字段是類對象,那么就得遞歸的去獲得其二進(jìn)制數(shù)據(jù),如果字段是基礎(chǔ)類型,那么通過調(diào)用其getString()就能獲得其數(shù)值的字符串形式。

在獲取字段類型前,我們還需要知道字段的修飾屬性,例如是public還是private,是不是static等,這些屬性通過Field類的接口getModifier()獲得,調(diào)用它會(huì)返回一個(gè)整形值,該值在相關(guān)比特位上設(shè)置1或0來表示修飾屬性。在java語法中共有11種修飾屬性,因此有11個(gè)比特位來對應(yīng),但我們不需要分析哪個(gè)比特位設(shè)置為1來獲取字段屬性,java反射提供了一個(gè)特定類Modifier,通過getModifier返回的數(shù)值可以輸入Modifier類的isPublic, isPrivate等接口來查詢字段對應(yīng)的修飾屬性。

在序列化時(shí),我們要忽略掉static屬性的字段,因此他們是寫死的,因此通過Modifier.isStatic(field.getModifier())所得結(jié)果就能進(jìn)行字段的static屬性判斷。總所周知,對于protected 或是private類型的字段,外部是不能直接讀取的,但是序列化必須要能讀取這類字段的值,要不然序列化就無法進(jìn)行,F(xiàn)ield類提供了setAccessible(true)來打破這個(gè)限制。

此外還需要考慮的一個(gè)因素是,如果字段是數(shù)組類型的情況。java反射提供了元類Array來應(yīng)對,假設(shè)實(shí)例對象obj是一個(gè)數(shù)組,那么Array.getLength(obj)就能獲得數(shù)組的長度,Array.get(obj, i)就能獲得第i個(gè)元素對象。

最后我們需要考慮序列化后的文件格式,我們使用xml格式來存儲(chǔ)序列化的結(jié)果,例如在上面例子中,字段a在序列化后對應(yīng)為”\1\“,具體的情況我們在后續(xù)代碼中慢慢來觀察。

首先我們使用IntelliJ 創(chuàng)建一個(gè)maven項(xiàng)目,由于我們需要將數(shù)據(jù)序列化成XML文件,因此需要使用JDOM接口,于是在pom.xml中添加如下依賴:

  1. <!-- https://mvnrepository.com/artifact/org.jdom/jdom --> 
  2.       <dependency> 
  3.           <groupId>org.jdom</groupId> 
  4.           <artifactId>jdom</artifactId> 
  5.           <version>2.0.2</version> 
  6.       </dependency> 

 

然后點(diǎn)擊一下Maven命令面板的下載按鈕下載jdom包。然后我們創(chuàng)建一個(gè)實(shí)現(xiàn)文件叫ReflectionSerilization,然后先完成一些骨架基礎(chǔ):

  1. import org.jdom2.Document; 
  2. import org.jdom2.Element; 
  3. import org.jdom2.output.XMLOutputter; 
  4. import java.lang.reflect.*; 
  5. import java.util.IdentityHashMap; 
  6. import java.util.HashMap; 
  7. import java.util.List; 
  8. import java.util.Properties; 
  9. import java.util.Map; 
  10.  
  11. public class ReflectionSerialization { 
  12.     public  Document doSerilizeObject(Object objectToSerilized) throws Exception{ 
  13.         return recursiveSerilizeObject(objectToSerilized, new Document(new Element("serialized")), 
  14.                 new IdentityHashMap()); 
  15.     } 
  16.  
  17.     private  Document recursiveSerilizeObject(Object objToSerilized, Document target, Map table) throws Exception{ 
  18.         String id = Integer.toString(table.size()); //為當(dāng)前序列化的對象設(shè)置id標(biāo)號 
  19.         table.put(objToSerilized, id); 
  20.         Class objClass = objToSerilized.getClass(); 
  21.  
  22.         Element elem = new Element("object"); 
  23.         elem.setAttribute("class", objClass.getName()); 
  24.         elem.setAttribute("id", id); 
  25.         target.getRootElement().addContent(elem); 
  26.         /* 
  27.          判斷當(dāng)前要序列化的對象是否是數(shù)組類型,如果不是,那么先遍歷該對象所有字段,然后遞歸的序列化對應(yīng)字段,因?yàn)樽侄斡锌赡苁穷悓ο螅?nbsp;
  28.          如果是數(shù)組類型,那么遍歷其中每個(gè)元素,然后針對每個(gè)元素進(jìn)行序列化 
  29.          */ 
  30.         if (objClass.isArray()) { 
  31.             //TODO 
  32.         } else { 
  33.             //TODO 
  34.         } 
  35.     } 
  36.     public static void main(String[] args) { 
  37.     } 

接下來我們針對兩個(gè)TODO進(jìn)行實(shí)現(xiàn),如果當(dāng)前要序列化的對象不是數(shù)組,那么需要遍歷其所有字段,然后序列化各個(gè)字段,如果字段是類對象類型,那么還得遞歸的對他進(jìn)行處理,我們看代碼實(shí)現(xiàn):

  1.  /* 
  2.          判斷當(dāng)前要序列化的對象是否是數(shù)組類型,如果不是,那么先遍歷該對象所有字段,然后遞歸的序列化對應(yīng)字段,因?yàn)樽侄斡锌赡苁穷悓ο螅?nbsp;
  3.          如果是數(shù)組類型,那么遍歷其中每個(gè)元素,然后針對每個(gè)元素進(jìn)行序列化 
  4.          */ 
  5.         if (objClass.isArray()) { 
  6.             handleNoArrayField(objToSerilized, objClass, target, table, elem); 
  7.         } else { 
  8.             //TODO 
  9.         } 
  10.  
  11.     private void handleNoArrayField(Object objToSeerilized, Class cls, Document target, Map table, Element parent) throws Exception { 
  12.         Field[] fields = this.iterateClassFields(cls); 
  13.         for (int i = 0; i < fields.length; i++) { 
  14.             if (!Modifier.isPublic(fields[i].getModifiers())) { 
  15.                 fields[i].setAccessible(true); //如果不是公有字段,那么需要設(shè)置它的可讀取性 
  16.             } 
  17.  
  18.             Element fElt = new Element("field"); //針對該字段插入xml元素 
  19.             fElt.setAttribute("name", fields[i].getName()); 
  20.             Class declClass = fields[i].getDeclaringClass(); //獲取字段對應(yīng)的類 
  21.             fElt.setAttribute("declaringclass", declClass.getName()); 
  22.             Class fieldType = fields[i].getType(); //獲得該字段類型對應(yīng)的元類 
  23.             Object child = fields[i].get(objToSeerilized); //獲得字段對應(yīng)的實(shí)例對象 
  24.             if (Modifier.isTransient(fields[i].getModifiers())) { 
  25.                 child = null
  26.             } 
  27.  
  28.             fElt.addContent(extractContentFromField(fieldType, child, target, table)); 
  29.             parent.addContent(fElt); 
  30.         } 
  31.     } 
  32. private Element extractContentFromField(Class fieldType, Object child, Document target, Map table) throws Exception{ 
  33.         //將字段對應(yīng)的數(shù)據(jù)抽取出來 
  34.         if (child == null) { 
  35.             return new Element("null"); 
  36.         } 
  37.         else if (!fieldType.isPrimitive()) { 
  38.             Element reference = new Element("reference"); 
  39.             if (table.containsKey(child)) { 
  40.                 reference.setText(table.get(child).toString()); //任何基礎(chǔ)類型都繼承自O(shè)bject,他們都支持toString來將自身對應(yīng)的數(shù)據(jù)進(jìn)行字符串表達(dá) 
  41.             } 
  42.             else { 
  43.                 reference.setText(Integer.toString(table.size())); 
  44.                 recursiveSerilizeObject(child, target, table); //如果不是基礎(chǔ)類型,那么就遞歸的進(jìn)行序列化 
  45.             } 
  46.         } 
  47.  
  48.     } 
  49.  
  50.     private Field[] iterateClassFields(Class cls) { 
  51.         List fieldsList = new LinkedList(); //用隊(duì)列存儲(chǔ)對象所有字段 
  52.         while (cls != null) { 
  53.             Field[] fields = cls.getDeclaredFields(); //獲得當(dāng)前實(shí)例對應(yīng)類所聲明的所有字段 
  54.             for (int i = 0; i < fields.length; i++) { 
  55.                 if (!Modifier.isStatic(fields[i].getModifiers())) { 
  56.                     fieldsList.add(fields[i]); //如果字段不是static修飾那么就加入隊(duì)列 
  57.                 } 
  58.             } 
  59.  
  60.             cls = cls.getSuperclass(); //獲取父類然后遞歸的獲取字段 
  61.         } 
  62.  
  63.         Field[] retValue = new Field[fieldsList.size()]; 
  64.         return (Field[])fieldsList.toArray(); 
  65.     } 

我們先看第一種情況的實(shí)現(xiàn),首先遍歷當(dāng)前實(shí)例對應(yīng)類聲明的所有字段,將所有字段放入到一個(gè)隊(duì)列中然后再一一取出來進(jìn)行處理,這個(gè)功能的實(shí)現(xiàn)就在函數(shù)iterateClassFields,然后對取出的字段進(jìn)行判斷,看它是否具備public屬性,如果不具備,那么要想讀取它的內(nèi)容,我們需要調(diào)用setAccessible進(jìn)行設(shè)置,接下來還有判斷其是否是Transient類型,如果不是,那么就通過extractContentFromField來讀取字段包含的數(shù)據(jù)。

在extractContentFromField中,先判斷字段是否為基礎(chǔ)數(shù)據(jù)類型,如果是,由于基礎(chǔ)數(shù)據(jù)類型都實(shí)現(xiàn)了toString方法,于是我們可以用該方法獲得數(shù)據(jù)的字符串對應(yīng)內(nèi)容,然后寫入到xml文件中,如果它不是基礎(chǔ)類型,那么我們就調(diào)用recursiveSerilizeObject遞歸的去對他進(jìn)行序列化。

 

由于內(nèi)容相對燒腦,因此我們先在這里暫停,消化一下后再處理下一步,也就是應(yīng)對字段是數(shù)組類型的情況。

 

責(zé)任編輯:武曉燕 來源: Coding迪斯尼
相關(guān)推薦

2011-04-02 09:04:49

Java序列化

2017-05-26 10:15:39

Java高級特性反射

2021-01-20 08:24:38

序列化內(nèi)存對象

2009-03-10 13:38:01

Java序列化字節(jié)流

2018-03-19 10:20:23

Java序列化反序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2013-03-11 13:55:03

JavaJSON

2009-06-14 22:01:27

Java對象序列化反序列化

2011-06-01 15:05:02

序列化反序列化

2009-08-24 17:14:08

C#序列化

2011-03-04 09:25:51

Java序列化

2010-01-08 13:25:07

ibmdwXML

2011-06-01 15:18:43

Serializabl

2011-04-02 13:47:01

2016-11-24 12:07:42

Android萬能圓角ImageView

2023-12-13 13:49:52

Python序列化模塊

2009-08-06 11:16:25

C#序列化和反序列化

2011-05-18 15:20:13

XML

2010-03-19 15:54:21

Java Socket

2009-09-09 16:53:49

C# XmlSeria序列化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號