日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

简明扼要的反射入门教程

發布時間:2024/7/5 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 简明扼要的反射入门教程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

反射

反射作為RTTI語言(比如Java)的基礎之一被很多人所熟知,但是有些同學對反射本身還是懵懵懂懂的,不是很清楚它到底有什么用。今天這節課我們就對反射本身來一個通體的認知。

定義

反射所在的包為:java.lang.reflect

它的英文版定義是:Reflection allows programmatic access to information about the fields, methods and constructors of loaded classes。the use of reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.

By default, a reflected object is not accessible.

Setting the accessible flag in a reflected object permits sophisticated applications with sufficient privilege, such as Java Object Serialization or other persistence mechanisms, to manipulate objects in a manner that would normally be prohibited.

Java反射主要是指程序可以訪問或者修改它本身狀態或行為的一種能力,是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程序在運行時通過Reflection APIs取得任何一個已知名稱的class的內部信息,包括其modifiers(諸如public, static 等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于運行時改變fields內容或喚起methods。

PS: 因為反射機制與Class類聯系緊密,所以在學習反射之前需要先了解Class類是什么。

Android文檔中的反射定義:https://developer.android.google.cn/reference/java/lang/reflect/package-summary.html

作用

動態的訪問、修改類的成員,可以達到使用常規手段做不到的目的。

最常見的例子:一個類有一個私有的成員屬性,無法通過正常的手段(比如Get方法)獲取這個屬性的值,那么就需要通過反射來獲得它的值,

反射多用于框架和組件,通過反射可以寫出復用性高的通用程序。

比如我們所熟知的Android中的Activity就是通過反射實例化生成的。

public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {return (Activity)cl.loadClass(className).newInstance();}

最后這行代碼通過字符串形式的類路徑加載指定的Activity類到內存中,然后通過反射的newInstance()實例化Activity對象,最后通過向下轉型返回給調用者。

上面這段代碼位于android.app.Instrumentation內。同理,我們所熟知的Application,Service,ContentProvider,BroadcatReceiver也是通過這種方式生成的。

Java多態的偉大之處就從這里開始!

你可能會有疑惑,為什么不直接new呢?

如果是new方法,那么new只能實例化指定的類,也就是說,如果使用new,Android系統框架只能實例化某個Activity了。而如果通過反射,那么只要是Activity的子類,都可以通過此方法實例化,這也就是多態的精髓。

優點

反射涉及到了動態與靜態的概念:

  • 靜態編譯:在編譯時確定類型,綁定對象,即通過。
  • 動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了java的靈活性,體現了多態的應用,用以降低類之間的藕合性。

反射機制的優點是可以實現動態創建對象以及修改對象的結構,體現出很大的靈活性。

缺點

它的缺點是對性能有影響。使用反射基本上是一種解釋操作。由于用于字段和方法接入時反射要遠慢于直接代碼,反射在性能上會有所影響,但性能問題的程度取決于程序中是如何使用反射的。如果它作為程序運行中相對很少涉及的部分,緩慢的性能將不會是一個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得至關重要。所以,合理的使用反射將大大提高我們程序的通用性和復用性。

技術解析鋪墊

運行時類型識別(Run-time Type Identification, RTTI)主要有兩種方式,一種是我們在編譯時和運行時已經知道了所有的類型,另外一種是功能強大的”反射”機制。

要理解RTTI在Java中的工作原理,首先必須知道類型信息在運行時是如何表示的,這項工作是由”Class對象”完成的,它包含了與類有關的信息。類是程序的重要組成部分,每個類都有一個Class對象,每當編寫并編譯了一個新類就會產生一個Class對象,它被保存在一個同名的.class文件中。在運行時,當我們想生成這個類的對象時,運行這個程序的Java虛擬機(JVM)會確認這個類的Class對象是否已經加載,如果尚未加載,JVM就會根據類名查找.class文件,并將其載入,一旦這個類的Class對象被載入內存,它就被用來創建這個類的所有對象。一般的RTTI形式包括三種:

1.傳統的類型轉換。如”(Apple)Fruit”,由RTTI確保類型轉換的正確性,如果執行了一個錯誤的類型轉換,就會拋出一個ClassCastException異常。

2.通過Class對象來獲取對象的類型。如

Class c = Class.forName("Apple");Object o = c.newInstance();

3.通過關鍵字instanceof或Class.isInstance()方法來確定對象是否屬于某個特定類型的實例,準確的說,應該是instanceof / Class.isInstance()可以用來確定對象是否屬于某個特定類及其所有基類的實例,這和equals() / ==不一樣,它們用來比較兩個對象是否屬于同一個類的實例,沒有考慮繼承關系。

基本用法

以下分別展示了反射的基本用法:

類的獲取方式

針對我們所知的不同情況分別有3種方法獲取Class對象

  • 當已知類名的時候,通過”類名.class”獲得

  • 當已知對象的時候,通過”對象.getClass”獲得

  • 當已知包括包名在內的完整類名(假設為String格式)的時候,可通過”Class.forName(classPath)”或者”ClassLoader.loadClass(classPath)”獲得

比如我們有一個類,類的結構如下:

package com.sahadev;/*** Created by Sahadev on 2017/4/27.*/public class ClassABean {public boolean mFlag;private IMethod mIMethod;public ClassABean() {}public ClassABean(boolean mFlag, IMethod iMethod) {super();this.mFlag = mFlag;this.mIMethod = iMethod;}private void printBValue(){System.out.println("The mFlag = " + mFlag);} }

那么類ClassABean的字節碼的獲取方式有以下3種:

  • ClassABean.class;
  • new ClassABean().getClass();
  • Class.forName(“com.sahadev.ClassABean”);或者ClassLoader.loadClass(“com.sahadev.ClassABean”);

獲取到Class字節碼對象之后,我們就可以對其進行操作了。

通過無參構造方法實例化對象

通過無參構造的方式有兩種,一種是我們上面看到的,使用newInstance()方法,而另一種是獲得類的無參構造方法,然后通過無參構造方法創建對象。其中newInstance()方法默認調用的是無參構造方法,如果類沒有無參構造方法,則會有異常拋出。

這兩種方法的使用方式分別如下:

//通過newInstance()方法構造ClassABean instanceA = ClassABean.class.newInstance();//通過無參構造方法構造Constructor<ClassABean> constructor = ClassABean.class.getConstructor();//獲取無參構造方法ClassABean instanceB = constructor.newInstance();//實例化

通過有參構造方法實例化對象

通過有參構造方法實例化對象的方法如下:

Constructor<ClassABean> constructor = ClassABean.class.getConstructor(Boolean.class, ClassBBean.class);//獲取指定參數的構造方法ClassABean instanceB = constructor.newInstance(true, new ClassBBean());//通過對象參數實例化對象

上面的代碼等價于:

ClassABean instanceB = new ClassABean(true, new ClassBBean());

通過以上有參構造方法構造的對象,它們的成員屬性現在都已經被賦了值。其中屬性mFlag的值為true,屬性mIMethod的實際實現者為ClassBBean。

PS: 在我們的示例中提到的ClassBBean類與ClassCBean類都同樣實現了IMethod接口。

方法調用

從以上的示例中我們知道了如何通過反射來實例化一個對象,接下來我們通過反射來調用一下類的私有方法。

在ClassABean類中提供了一個私有方法printBValue(),我們看看如何通過反射來調用這個方法:

Method method = ClassABean.class.getDeclaredMethod("printBValue");ClassABean instanceB = new ClassABean(true,new ClassBBean());method.setAccessible(true);method.invoke(instanceB);

控制臺會正確輸出我們預想中的值:

The mFlag = true

這樣調用和我們通過普通方法調用的效果是一致的,只是反射可以調用類的私有方法。

在這里細心的同學就會發現,Class類本身提供了兩個獲取方法的方法,一個是getDeclaredMethod,另一個是getMethod。那這兩者有什么區別呢?getDeclaredMethod用于獲取所有的方法,包括私有方法。而getMethod則用于獲取public方法,其它權限方法無法獲得。

屬性獲取與賦值

屬性的獲取與方法類同:

Field flagField = ClassABean.class.getDeclaredField("mFlag");flagField.setAccessible(true);ClassABean classABeanInstance = new ClassABean(true, new ClassBBean());boolean flag = (boolean) flagField.get(classABeanInstance);

這樣就可以獲得對象classABeanInstance的mFlag的值,同樣的,我們還可以獲得屬性mIMethod的值:

Field iMethodField = ClassABean.class.getDeclaredField("mIMethod");iMethodField.setAccessible(true);ClassABean classABeanInstance = new ClassABean(true, new ClassBBean());IMethod iMethod = (IMethod) iMethodField.get(classABeanInstance);

其中IMethod的具體實例為ClassBBean對象。

接下來我們演示一下如何替換屬性的值,這種方式在很多地方都很常見,它的用途很廣:

Field iMethodField = ClassABean.class.getDeclaredField("mIMethod");iMethodField.setAccessible(true);ClassABean classABeanInstance = new ClassABean(true, new ClassBBean());ClassCBean classCBean = new ClassCBean();iMethodField.set(classABeanInstance, classCBean);IMethod iMethod = (IMethod) iMethodField.get(classABeanInstance);

通過這樣的方式,我們再獲取mIMethod的值將會是classCBean對象。我們在這里使用了set的方法,set方法用于給指定對象的屬性賦值。

用例1(修改TextView的autoLink的點擊實現)

相關文章介紹:如何修改TextView鏈接點擊實現(包含鏈接生成與點擊原理分析)

用例2(熱修復實現)

相關文章介紹:一步步手動實現熱修復

擴展了解

通過反射我們可以獲得一個類的注解,它的父類以及實現的接口等。了解反射可以有助于我們實現抽象能力更強的框架。

擴展閱讀:https://developer.android.google.cn/reference/java/lang/Class.html

參考地址

http://c.biancheng.net/cpp/html/1781.html
http://www.voidcn.com/blog/zbuger/article/p-5771880.html
http://www.fanyilun.me/2015/10/29/Java反射原理/
http://rednaxelafx.iteye.com/blog/548536
http://blog.csdn.net/u013551462/article/details/51261817

總結

以上是生活随笔為你收集整理的简明扼要的反射入门教程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。