詳解Java中的Reflection反射和暴力反射
1. 反射(Reflection)的概念
1.1 反射的出現(xiàn)背景
Java程序中,所有的對象都有兩種類型:編譯時類型
和運行時類型
,而很多時候?qū)ο蟮木幾g時類型和運行時類型不一致
。
例如:
Object obj = new String("hello"); obj.getClass()
如上:某些變量或形參的聲明類型是Object
類型,但是程序卻需要調(diào)用該對象運行時類型的方法,該方法不是Object中的方法,那么如何解決呢?
解決這個問題,有兩種方案:
方案1:在編譯和運行時都完全知道類型的具體信息,在這種情況下,我們可以直接先使用instanceof
運算符進行判斷,再利用強制類型轉(zhuǎn)換符將其轉(zhuǎn)換成運行時類型的變量即可。
方案2:編譯時根本無法預(yù)知該對象和類的真實信息,程序只能依靠運行時信息
來發(fā)現(xiàn)該對象和類的真實信息,這就必須使用反射。
1.2 反射概述
Reflection(反射)是被視為動態(tài)語言
的關(guān)鍵,反射機制允許程序在運行期間
借助于Reflection API取得任何類的內(nèi)部信息,并能直接操作任意對象的內(nèi)部屬性及方法。
加載完類之后,在堆內(nèi)存的方法區(qū)中就產(chǎn)生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結(jié)構(gòu)信息。此時就可以通過這個對象看到類的結(jié)構(gòu)。這個對象就像一面鏡子,透過這個鏡子看到類的結(jié)構(gòu),所以,我們形象的稱之為:反射。
從內(nèi)存加載上看反射:
1.3 Java反射機制研究及應(yīng)用
Java反射機制提供的功能:
- 在運行時判斷任意一個對象所屬的類
- 在運行時構(gòu)造任意一個類的對象
- 在運行時判斷任意一個類所具有的成員變量和方法
- 在運行時獲取泛型信息
- 在運行時調(diào)用任意一個對象的成員變量和方法
- 在運行時處理注解
- 生成動態(tài)代理
1.4 反射相關(guān)的主要API
java.lang.Class
:代表一個類java.lang.reflect.Method
:代表類的方法java.lang.reflect.Field
:代表類的成員變量java.lang.reflect.Constructor
:代表類的構(gòu)造器......
1.5 反射的優(yōu)缺點
優(yōu)點:
- 提高了Java程序的靈活性和擴展性,
降低了耦合性
,提高自適應(yīng)
能力 - 允許程序創(chuàng)建和控制任何類的對象,無需提前
硬編碼
目標(biāo)類
缺點:
- 反射的
性能較低
。- 反射機制主要應(yīng)用在對靈活性和擴展性要求很高的系統(tǒng)框架上
- 反射會
模糊
程序內(nèi)部邏輯,可讀性較差
。
2. Class類并獲取Class實例
要想解剖
一個類,必須先要獲取到該類的Class對象。而剖析一個類或用反射解決具體的問題就是使用相關(guān)API:
java.lang.Class
java.lang.reflect.*
所以,Class對象是反射的根源。
2.1 理解Class
2.1.1 理論上
在Object類中定義了以下的方法,此方法將被所有子類繼承:
public final Class getClass()
以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結(jié)果來看也很好理解,即:可以通過對象反射求出類的名稱。
對象照鏡子后可以得到的信息:某個類的屬性、方法和構(gòu)造器、某個類到底實現(xiàn)了哪些接口。對于每個類而言,JRE 都為其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個結(jié)構(gòu)(class
/interface
/enum
/annotation
/primitive
type
/void
/[]
)的有關(guān)信息。
- Class本身也是一個類
- Class 對象只能由系統(tǒng)建立對象
- 一個加載的類在 JVM 中只會有一個Class實例
- 一個Class對象對應(yīng)的是一個加載到JVM中的一個.class文件
- 每個類的實例都會記得自己是由哪個 Class 實例所生成
- 通過Class可以完整地得到一個類中的所有被加載的結(jié)構(gòu)
- Class類是Reflection的根源,針對任何你想動態(tài)加載、運行的類,唯有先獲得相應(yīng)的Class對象
2.1.2 內(nèi)存結(jié)構(gòu)上
說明:上圖中字符串常量池在JDK6中存儲在方法區(qū);JDK7及以后,存儲在堆空間。
2.2 獲取Class類的實例(四種方法)
方式1:要求編譯期間已知類型
前提:若已知具體的類,通過類的class
屬性獲取,該方法最為安全可靠,程序性能最高
實例:
Class clazz = String.class;
方式2:獲取對象的運行時類型
前提:已知某個類的實例,調(diào)用該實例的getClass()
方法獲取Class
對象
實例:
Class clazz = "www.example.com".getClass();
方式3:可以獲取編譯期間未知的類型
前提:已知一個類的全類名,且該類在類路徑下,可通過Class
類的靜態(tài)方法forName()
獲取,可能拋出ClassNotFoundException
實例:
Class clazz = Class.forName("java.lang.String");
方式4:其他方式(不做要求)
前提:可以用系統(tǒng)類加載對象或自定義加載器對象加載指定路徑下的類型
實例:
ClassLoader cl = this.getClass().getClassLoader(); Class clazz4 = cl.loadClass("類的全類名");
再舉例:
public class GetClassObject { @Test public void test01() throws ClassNotFoundException{ Class c1 = GetClassObject.class; GetClassObject obj = new GetClassObject(); Class c2 = obj.getClass(); Class c3 = Class.forName("com.example.classtype.GetClassObject"); Class c4 = ClassLoader.getSystemClassLoader().loadClass("com.example.classtype.GetClassObject"); System.out.println("c1 = " + c1); System.out.println("c2 = " + c2); System.out.println("c3 = " + c3); System.out.println("c4 = " + c4); System.out.println(c1 == c2); System.out.println(c1 == c3); System.out.println(c1 == c4); } }
2.3 哪些類型可以有Class對象
簡而言之,所有Java類型!
(1)class
:外部類,成員(成員內(nèi)部類,靜態(tài)內(nèi)部類),局部內(nèi)部類,匿名內(nèi)部類
(2)interface
:接口
(3)[]
:數(shù)組
(4)enum
:枚舉
(5)annotation
:注解@interface
(6)primitive type
:基本數(shù)據(jù)類型
(7)void
舉例:
Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // 只要元素類型與維度一樣,就是同一個Class System.out.println(c10 == c11);
2.4 Class類的常用方法
方法名 | 功能說明 |
---|---|
static Class forName (String name) | 返回指定類名 name 的 Class 對象 |
Object newInstance () | 調(diào)用缺省構(gòu)造函數(shù),返回該Class 對象的一個實例 |
getName () | 返回此Class 對象所表示的實體(類、接口、數(shù)組類、基本類型或void )名稱 |
Class getSuperClass () | 返回當(dāng)前Class 對象的父類的Class對象 |
Class [] getInterfaces () | 獲取當(dāng)前Class 對象的接口 |
ClassLoader getClassLoader () | 返回該類的類加載器 |
Class getSuperclass () | 返回表示此Class所表示的實體的超類的Class |
Constructor[] getConstructors () | 返回一個包含某些Constructor 對象的數(shù)組 |
Field[] getDeclaredFields () | 返回Field 對象的一個數(shù)組 |
Method getMethod (String name,Class … paramTypes) | 返回一個Method 對象,此對象的形參類型為paramType |
舉例:
String str = "test4.Person"; Class clazz = Class.forName(str); Object obj = clazz.newInstance(); Field field = clazz.getField("name"); field.set(obj, "Peter"); Object name = field.get(obj); System.out.println(name); //注:test4.Person是test4包下的Person類
到此這篇關(guān)于詳解Java中的Reflection反射和暴力反射的文章就介紹到這了,更多相關(guān)Java Reflection反射和暴力反射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中過濾器 (Filter) 和 攔截器 (Interceptor)的使用
這篇文章主要介紹了Java中過濾器 (Filter) 和 攔截器 (Interceptor)的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05java8 stream的多字段排序?qū)崿F(xiàn)(踩坑)
這篇文章主要介紹了java8 stream的多字段排序?qū)崿F(xiàn)(踩坑),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03jenkins如何通過pipeline部署springboot項目
為了提高SpringBoot項目的部署效率和規(guī)范性,建議將項目代碼和部署腳本分離,項目代碼倉庫專注業(yè)務(wù)邏輯,構(gòu)建為jar包;另外設(shè)立獨立代碼倉庫存放Jenkinsfile等部署配置文件,在Jenkins中配置pipeline,自動拉取項目代碼進行構(gòu)建和部署2024-09-09Java中調(diào)用Python的實現(xiàn)示例
本文主要介紹了Java中調(diào)用Python的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05使用lombok注解導(dǎo)致mybatis-plus TypeHandler失效的解決
這篇文章主要介紹了使用lombok注解導(dǎo)致mybatis-plus TypeHandler失效的解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07