成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[437080]]

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

  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的實例,那么通常需要將變量a,b的數(shù)值對應的二進制數(shù)寫入,然后獲得類B實例序列化后的二進制數(shù)據(jù),最后將變量c的數(shù)值的二進制數(shù)據(jù),這里可以體會到,序列化其實有一種遞歸的性質,在序列化過程中如果遇到的是基礎類型,那么可以直接獲取其對應的二進制數(shù)據(jù),如果遇到類實例,那么需要先序列化它,取得對應二進制數(shù)據(jù)。

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

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

1,獲得要序列化的類實例;

2,獲得類中各個字段的屬性,類型等相關信息。

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

4,如果對應字段是一個類實例,那么先遞歸的序列化該實例

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

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

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

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

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

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

首先我們使用IntelliJ 創(chuàng)建一個maven項目,由于我們需要將數(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> 

 

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

  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()); //為當前序列化的對象設置id標號 
  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.          判斷當前要序列化的對象是否是數(shù)組類型,如果不是,那么先遍歷該對象所有字段,然后遞歸的序列化對應字段,因為字段有可能是類對象, 
  28.          如果是數(shù)組類型,那么遍歷其中每個元素,然后針對每個元素進行序列化 
  29.          */ 
  30.         if (objClass.isArray()) { 
  31.             //TODO 
  32.         } else { 
  33.             //TODO 
  34.         } 
  35.     } 
  36.     public static void main(String[] args) { 
  37.     } 

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

  1.  /* 
  2.          判斷當前要序列化的對象是否是數(shù)組類型,如果不是,那么先遍歷該對象所有字段,然后遞歸的序列化對應字段,因為字段有可能是類對象, 
  3.          如果是數(shù)組類型,那么遍歷其中每個元素,然后針對每個元素進行序列化 
  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); //如果不是公有字段,那么需要設置它的可讀取性 
  16.             } 
  17.  
  18.             Element fElt = new Element("field"); //針對該字段插入xml元素 
  19.             fElt.setAttribute("name", fields[i].getName()); 
  20.             Class declClass = fields[i].getDeclaringClass(); //獲取字段對應的類 
  21.             fElt.setAttribute("declaringclass", declClass.getName()); 
  22.             Class fieldType = fields[i].getType(); //獲得該字段類型對應的元類 
  23.             Object child = fields[i].get(objToSeerilized); //獲得字段對應的實例對象 
  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.         //將字段對應的數(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()); //任何基礎類型都繼承自Object,他們都支持toString來將自身對應的數(shù)據(jù)進行字符串表達 
  41.             } 
  42.             else { 
  43.                 reference.setText(Integer.toString(table.size())); 
  44.                 recursiveSerilizeObject(child, target, table); //如果不是基礎類型,那么就遞歸的進行序列化 
  45.             } 
  46.         } 
  47.  
  48.     } 
  49.  
  50.     private Field[] iterateClassFields(Class cls) { 
  51.         List fieldsList = new LinkedList(); //用隊列存儲對象所有字段 
  52.         while (cls != null) { 
  53.             Field[] fields = cls.getDeclaredFields(); //獲得當前實例對應類所聲明的所有字段 
  54.             for (int i = 0; i < fields.length; i++) { 
  55.                 if (!Modifier.isStatic(fields[i].getModifiers())) { 
  56.                     fieldsList.add(fields[i]); //如果字段不是static修飾那么就加入隊列 
  57.                 } 
  58.             } 
  59.  
  60.             cls = cls.getSuperclass(); //獲取父類然后遞歸的獲取字段 
  61.         } 
  62.  
  63.         Field[] retValue = new Field[fieldsList.size()]; 
  64.         return (Field[])fieldsList.toArray(); 
  65.     } 

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

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

 

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

 

責任編輯:武曉燕 來源: Coding迪斯尼
相關推薦

2011-04-02 09:04:49

Java序列化

2021-01-20 08:24:38

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

2017-05-26 10:15:39

Java高級特性反射

2009-03-10 13:38:01

Java序列化字節(jié)流

2022-08-06 08:41:18

序列化反序列化Hessian

2018-03-19 10:20:23

Java序列化反序列化

2013-03-11 13:55:03

JavaJSON

2016-11-24 12:07:42

Android萬能圓角ImageView

2009-06-14 22:01:27

Java對象序列化反序列化

2011-06-01 15:05:02

序列化反序列化

2009-08-24 17:14:08

C#序列化

2010-01-08 13:25:07

ibmdwXML

2011-06-01 15:18:43

Serializabl

2011-04-02 13:47:01

2011-03-04 09:25:51

Java序列化

2023-04-06 13:47:47

2022-06-23 18:10:15

多云

2009-02-27 13:48:00

Mdaemon郵件服務器

2009-08-06 11:16:25

C#序列化和反序列化

2011-05-18 15:20:13

XML
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 老牛影视av一区二区在线观看 | 日韩在线免费播放 | 华丽的挑战在线观看 | 全免费a级毛片免费看视频免 | 成人在线视频免费观看 | 精品国产高清一区二区三区 | 欧美中文字幕在线观看 | 亚洲天堂精品一区 | 国产网站在线免费观看 | 国产精品国产三级国产aⅴ原创 | 国产精品极品美女在线观看免费 | 免费成人午夜 | 精品国产一区二区三区性色av | 视频1区2区 | 亚洲免费人成在线视频观看 | 欧美黑人一级爽快片淫片高清 | 日韩中出 | 成人影院在线 | 亚洲欧美在线视频 | 亚洲成人精品 | 狠狠躁18三区二区一区 | 欧美三级成人理伦 | 在线黄av| 欧美日韩国产精品激情在线播放 | 密色视频| 在线视频国产一区 | 国产精品免费在线 | 免费看欧美一级片 | 日本成人毛片 | 成人在线视频免费观看 | 天天操天天射天天 | 成年视频在线观看福利资源 | 一级电影免费看 | 刘亦菲国产毛片bd | www.99re| 日韩av手机在线观看 | h视频免费在线观看 | 久久精品综合 | 欧美一区二区三区在线播放 | 麻豆国产一区二区三区四区 | 中文字幕综合在线 |