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