偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

高級(jí) Java 思考筆記之反射的基本原理之一

開發(fā) 后端
原來(lái)我一直將java當(dāng)做不用delete的c++來(lái)用,但是最近在工作上遇到一些問(wèn)題,發(fā)現(xiàn)這樣的做法不管用,因?yàn)楣ぷ魃嫌龅降膉ava代碼采用了很多框架,例如spring boot之類。當(dāng)我想深入了解這些框架的設(shè)計(jì)原理時(shí)發(fā)現(xiàn),如果不了解java的高級(jí)語(yǔ)法特性,這些框架的設(shè)計(jì)思維或運(yùn)行方式根本就無(wú)法理解。

[[432232]]

原來(lái)我一直將java當(dāng)做不用delete的c++來(lái)用,但是最近在工作上遇到一些問(wèn)題,發(fā)現(xiàn)這樣的做法不管用,因?yàn)楣ぷ魃嫌龅降膉ava代碼采用了很多框架,例如spring boot之類。當(dāng)我想深入了解這些框架的設(shè)計(jì)原理時(shí)發(fā)現(xiàn),如果不了解java的高級(jí)語(yǔ)法特性,這些框架的設(shè)計(jì)思維或運(yùn)行方式根本就無(wú)法理解。

后來(lái)稍微調(diào)查一下發(fā)現(xiàn),自從java8之后,java的語(yǔ)法發(fā)生了巨大的變化,代碼的設(shè)計(jì)模式也不再像以前“不用delete的c++”,參照原來(lái)c++的思維去解讀java代碼已經(jīng)行不通了,于是重新學(xué)習(xí),順便在學(xué)習(xí)和思考中做一些總結(jié)輸出。

java新增語(yǔ)法特征中,有較為抽象的一部分叫反射,其實(shí)說(shuō)白了就是用程序來(lái)控制程序。java將一切概念都包含在類中,于是對(duì)應(yīng)每一個(gè)具體類,語(yǔ)言體系還為特定的具體類生成了描述其特性的抽象類,例如我們定義了如下一個(gè)類:

  1. static public class HelloWorld { 
  2.     public void sayHelloWorld1(String s) { 
  3.            System.out.println("say hello world 1!: " + s); 
  4.        } 
  5.  
  6.        public void sayHelloWorld2(int i) { 
  7.            System.out.println("say hello world 2: " + i); 
  8.        } 
  9.  
  10.        protected  void sayHelloWorld3() { 
  11.            System.out.println("say hello world3"); 
  12.        } 
  13.  
  14.        private void sayHelloworld4() { 
  15.            System.out.println("say hello world4"); 
  16.        } 
  17.  
  18.        public String field1 = "hello"
  19.        protected int field2 = 1; 
  20.        private int field3 = 2; 
  21.  
  22.    } 

這個(gè)類里面有公有,保護(hù),私有等幾個(gè)方法,虛擬機(jī)在為這個(gè)類生成字節(jié)碼時(shí),還構(gòu)建了另一個(gè)類,用于描述這個(gè)類的類,可以稱為它的源類,后者用來(lái)描述前者在編程語(yǔ)言上的特性,例如它包含了哪些方法,這些方法對(duì)應(yīng)的參數(shù),返回值,公有私有等,如果把一個(gè)類比作人,那么反射其實(shí)就是x光掃描,它把你內(nèi)外的具體細(xì)節(jié)都暴露出來(lái),我們看看怎么用反射來(lái)解讀上面定義的類:

  1. public static void manipulateHelloWorldClass(Object obj) { 
  2.         Class cls = obj.getClass(); 
  3.         //打印出類對(duì)象對(duì)應(yīng)的類名 
  4.         System.out.println("object class name: " + cls.getName()); 
  5.         //返回實(shí)例對(duì)應(yīng)的類所聲明的所有函數(shù) 
  6.         Method[] methods = cls.getDeclaredMethods(); 
  7.         for (Method method : methods) { 
  8.             System.out.println("delcared method name: " +  method.getName()); 
  9.         } 
  10.     } 
  11.  
  12.     public static void main(String[] args) { 
  13.         HelloWorld helloWorld = new HelloWorld(); 
  14.         manipulateHelloWorldClass(helloWorld); 
  15.     } 

上面代碼運(yùn)行后輸出結(jié)果如下:

可以看到,代碼打印出helloword實(shí)例對(duì)象在編程語(yǔ)言上的信息,例如上面代碼就打印出它對(duì)應(yīng)類的名字,都有哪些方法等等,在Java語(yǔ)言中Class類也叫原類,它用來(lái)解析所有實(shí)例對(duì)應(yīng)類在編程語(yǔ)法上的信息,每個(gè)實(shí)例對(duì)應(yīng)的類都能得到一個(gè)Class類對(duì)應(yīng)的實(shí)例,就像代碼中那樣,這個(gè)實(shí)例能夠查詢對(duì)應(yīng)類有哪些方法,定義了哪些字段.

代碼中調(diào)用了getDeclaredMethods來(lái)獲得實(shí)例所對(duì)應(yīng)類在定義時(shí)聲明的所有方法,Class類還有一個(gè)方法叫g(shù)etMethods,它返回實(shí)例對(duì)應(yīng)類自己在定義時(shí)所聲明的公有方法,以及繼承過(guò)來(lái)的所有公有方法。

反射機(jī)制一個(gè)很重要的作用就是能夠查詢給定實(shí)例是否有特定接口,然后調(diào)用相關(guān)接口,代碼如下:

  1. public static void callMethod(Object obj) { 
  2.         Class cls = obj.getClass(); 
  3.         try { 
  4.             Method method = cls.getMethod("sayHelloWorld1", new Class[] {String.class}); 
  5.             method.invoke(obj, new Object[]{"hello world"}); 
  6.             method = cls.getMethod("sayHelloWorld2", new Class[] {int.class}); 
  7.             method.invoke(obj, new Object[]{new Integer(123)}); 
  8.         } catch(NoSuchMethodException e) { 
  9.             e.printStackTrace();; 
  10.         } catch (IllegalAccessException e) { 
  11.             throw new IllegalArgumentException("Insufficient access permissions to call" 
  12.                     + "setColor(:Color) in class " + cls.getName()); 
  13.         } catch (InvocationTargetException ex) { 
  14.             throw new RuntimeException(ex); 
  15.         } 
  16.  
  17.     } 
  18.  
  19.     public static void main(String[] args) { 
  20.         HelloWorld helloWorld = new HelloWorld(); 
  21.       //  manipulateHelloWorldClass(helloWorld); 
  22.         callMethod(helloWorld); 
  23.     } 

上面代碼執(zhí)行后結(jié)果如下所示:

我們可以體會(huì)到,c++是不會(huì)有這種特性的,java由于具備了這種反射機(jī)制,使得它能夠用來(lái)開發(fā)很多框架,在java世界里形形色色的框架特別多,這跟它在語(yǔ)法上支持反射不無(wú)關(guān)系,類似spring boot這些java程序員絕對(duì)必須要掌握的框架,它的設(shè)計(jì)就大量使用了反射機(jī)制。

使用反射功能還可以很好的實(shí)現(xiàn)類實(shí)例的序列化,當(dāng)我們想要將一個(gè)類實(shí)例的信息從內(nèi)存存儲(chǔ)到硬盤時(shí),我們就需要將類實(shí)例當(dāng)前內(nèi)部各個(gè)字段的信息存儲(chǔ)到文件里,以后需要的時(shí)候再?gòu)奈募凶x出,然后利用讀到的數(shù)據(jù)重新把類的實(shí)例new出來(lái),因此實(shí)現(xiàn)序列化第一步就是要獲得類實(shí)例所有字段的數(shù)據(jù),相應(yīng)代碼如下:

  1. import java.lang.reflect.*; 
  2. import java.util.*; 
  3.  
  4. public class Serializer { 
  5.     public static Field[] getInstanceVariable(Object obj) { 
  6.         Class cls = obj.getClass(); 
  7.         List accumFields = new LinkedList(); 
  8.         while (cls != null) { 
  9.             //獲得實(shí)例對(duì)應(yīng)類所聲明的全部字段 
  10.             Field[] fields = cls.getDeclaredFields(); 
  11.             for (Field field : fields) { 
  12.                 accumFields.add(field); //將字段對(duì)應(yīng)的元類對(duì)象存儲(chǔ)起來(lái) 
  13.             } 
  14.  
  15.             cls = cls.getSuperclass(); //獲得父類對(duì)象 
  16.         } 
  17.     } 

代碼中要注意到,實(shí)例化一個(gè)類實(shí)例時(shí),還需要考慮這個(gè)類的繼承關(guān)系,如果它有父類的話,我們還需要取得其父類的字段對(duì)應(yīng)的信息,所以代碼中使用getSupperclass方法獲得實(shí)例對(duì)應(yīng)類的父類的源類對(duì)象。

前面代碼中我們看到,F(xiàn)ield類對(duì)應(yīng)的getModifier能返回字段的修飾屬性,也就是字段是public, private, protected , static, native等等,它返回的是一個(gè)2的指數(shù)冪數(shù)值,實(shí)際上它對(duì)應(yīng)一個(gè)16比特位數(shù)值,當(dāng)字段屬于哪種情況,就在相應(yīng)的比特位上設(shè)置為1,因?yàn)樽侄蔚膶傩钥偣灿?6種,因此getModifier返回一個(gè)2字節(jié)的整數(shù)。

得到這個(gè)數(shù)值后,我們?cè)僬{(diào)用一系列方法獲得其屬性,例如isPublic返回字段是否是public類型,isPrivate返回字段是否為private類型等。由于我們?cè)谛蛄谢粋€(gè)實(shí)例時(shí),不用關(guān)注那些靜態(tài)變量,因?yàn)殪o態(tài)變量的值是寫死的,因此需要對(duì)上面的代碼進(jìn)行修改,忽略掉那些被static修飾的字段:

  1. import java.lang.reflect.*; 
  2. import java.util.*; 
  3.  
  4. public class Serializer { 
  5.     public static Field[] getInstanceVariable(Object obj) { 
  6.         Class cls = obj.getClass(); 
  7.         List accumFields = new LinkedList(); 
  8.         while (cls != null) { 
  9.             //獲得實(shí)例對(duì)應(yīng)類所聲明的全部字段 
  10.             Field[] fields = cls.getDeclaredFields(); 
  11.             for (Field field : fields) { 
  12.                 //確保字段不是static類型 
  13.                 if(!Modifier.isStatic(field.getModifiers())) { 
  14.                     accumFields.add(field); 
  15.                 } 
  16.             } 
  17.  
  18.             cls = cls.getSuperclass(); //獲得父類對(duì)象 
  19.         } 
  20.  
  21.         Field[] retvalue = new Field[accumFields.size()]; 
  22.         return (Field[])accumFields.toArray(retvalue); 
  23.     } 

這里還有很多問(wèn)題需要考慮,例如序列化實(shí)例時(shí),我們需要獲取類實(shí)例里面字段的值,但是如果字段屬性是private或者protected時(shí),我們就不能直接從類實(shí)例中讀取字段內(nèi)容,同時(shí)如果字段對(duì)應(yīng)的是數(shù)組類型,那么我們還得采取特定的處理方法,為了防止文章長(zhǎng)度過(guò)長(zhǎng)令人看不下去,我們把這些內(nèi)容總結(jié)再下一篇。

 

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

2012-01-12 14:37:34

jQuery

2016-08-17 23:53:29

網(wǎng)絡(luò)爬蟲抓取系統(tǒng)

2010-08-20 13:29:33

OFDM

2013-04-07 14:09:55

Android應(yīng)用基本

2020-03-21 14:57:14

手機(jī)定位智能手機(jī)APP

2009-02-24 09:43:00

IP電話原理

2011-11-29 12:17:00

2010-03-18 20:13:03

Java socket

2021-04-27 19:21:48

HBase原理開源

2010-03-17 13:35:02

2019-11-28 10:45:28

ZooKeeper源碼分布式

2016-08-18 00:04:09

網(wǎng)絡(luò)爬蟲抓取系統(tǒng)服務(wù)器

2021-02-08 21:40:04

SockmapBPF存儲(chǔ)

2011-08-10 19:33:09

Cocoa對(duì)象

2020-10-14 06:23:54

SpringBean實(shí)例化

2010-06-18 17:28:37

Linux Anacr

2020-11-26 13:54:03

容器LinuxDocker

2011-07-07 14:10:21

Cocoa 內(nèi)省 hash

2009-06-11 09:56:09

MySQL Repli原理

2020-12-29 16:55:44

ZooKeeper運(yùn)維數(shù)據(jù)結(jié)構(gòu)
點(diǎn)贊
收藏

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