Java 反射機(jī)制原理與用法詳解
本文實(shí)例講述了Java 反射機(jī)制原理與用法。分享給大家供大家參考,具體如下:
反射反射,程序員的快樂(lè)!
1、什么是反射?
Java反射就是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;并且能改變它的屬性。而這也是Java被視為動(dòng)態(tài)(或準(zhǔn)動(dòng)態(tài),為啥要說(shuō)是準(zhǔn)動(dòng)態(tài),因?yàn)橐话愣缘膭?dòng)態(tài)語(yǔ)言定義是程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類(lèi)型,這種語(yǔ)言稱(chēng)為動(dòng)態(tài)語(yǔ)言。從這個(gè)觀點(diǎn)看,Perl,Python,Ruby是動(dòng)態(tài)語(yǔ)言,C++,Java,C#不是動(dòng)態(tài)語(yǔ)言。)語(yǔ)言的一個(gè)關(guān)鍵性質(zhì)。
2、反射能做什么?
我們知道反射機(jī)制允許程序在運(yùn)行時(shí)取得任何一個(gè)已知名稱(chēng)的class的內(nèi)部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,并可于運(yùn)行時(shí)改變fields內(nèi)容或調(diào)用methods。那么我們便可以更靈活的編寫(xiě)代碼,代碼可以在運(yùn)行時(shí)裝配,無(wú)需在組件之間進(jìn)行源代碼鏈接,降低代碼的耦合度;還有動(dòng)態(tài)代理的實(shí)現(xiàn)等等;但是需要注意的是反射使用不當(dāng)會(huì)造成很高的資源消耗!
3、反射的具體實(shí)現(xiàn)
下面是一個(gè)基本的類(lèi) Person
package com.ys.reflex;
public class Person {
//私有屬性
private String name = "Tom";
//公有屬性
public int age = 18;
//構(gòu)造方法
public Person() {
}
//私有方法
private void say(){
System.out.println("private say()...");
}
//公有方法
public void work(){
System.out.println("public work()...");
}
}
①、得到 Class 的三種方式
//1、通過(guò)對(duì)象調(diào)用 getClass() 方法來(lái)獲取,通常應(yīng)用在:比如你傳過(guò)來(lái)一個(gè) Object
// 類(lèi)型的對(duì)象,而我不知道你具體是什么類(lèi),用這種方法
Person p1 = new Person();
Class c1 = p1.getClass();
//2、直接通過(guò) 類(lèi)名.class 的方式得到,該方法最為安全可靠,程序性能更高
// 這說(shuō)明任何一個(gè)類(lèi)都有一個(gè)隱含的靜態(tài)成員變量 class
Class c2 = Person.class;
//3、通過(guò) Class 對(duì)象的 forName() 靜態(tài)方法來(lái)獲取,用的最多,
// 但可能拋出 ClassNotFoundException 異常
Class c3 = Class.forName("com.ys.reflex.Person");
需要注意的是:一個(gè)類(lèi)在 JVM 中只會(huì)有一個(gè) Class 實(shí)例,即我們對(duì)上面獲取的 c1,c2,c3進(jìn)行 equals 比較,發(fā)現(xiàn)都是true
②、通過(guò) Class 類(lèi)獲取成員變量、成員方法、接口、超類(lèi)、構(gòu)造方法等
查閱 API 可以看到 Class 有很多方法:
- getName():獲得類(lèi)的完整名字。
- getFields():獲得類(lèi)的public類(lèi)型的屬性。
- getDeclaredFields():獲得類(lèi)的所有屬性。包括private 聲明的和繼承類(lèi)
- getMethods():獲得類(lèi)的public類(lèi)型的方法。
- getDeclaredMethods():獲得類(lèi)的所有方法。包括private 聲明的和繼承類(lèi)
- getMethod(String name, Class[] parameterTypes):獲得類(lèi)的特定方法,name參數(shù)指定方法的名字,parameterTypes 參數(shù)指定方法的參數(shù)類(lèi)型。
- getConstructors():獲得類(lèi)的public類(lèi)型的構(gòu)造方法。
- getConstructor(Class[] parameterTypes):獲得類(lèi)的特定構(gòu)造方法,parameterTypes 參數(shù)指定構(gòu)造方法的參數(shù)類(lèi)型。
- newInstance():通過(guò)類(lèi)的不帶參數(shù)的構(gòu)造方法創(chuàng)建這個(gè)類(lèi)的一個(gè)對(duì)象。
我們通過(guò)一個(gè)例子來(lái)綜合演示上面的方法:
//獲得類(lèi)完整的名字
String className = c2.getName();
System.out.println(className);//輸出com.ys.reflex.Person
//獲得類(lèi)的public類(lèi)型的屬性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//獲得類(lèi)的所有屬性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//獲得類(lèi)的public類(lèi)型的方法。這里包括 Object 類(lèi)的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//獲得類(lèi)的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//獲得指定的屬性
Field f1 = c2.getField("age");
System.out.println(f1);
//獲得指定的私有屬性
Field f2 = c2.getDeclaredField("name");
//啟用和禁用訪(fǎng)問(wèn)安全檢查的開(kāi)關(guān),值為 true,則表示反射的對(duì)象在使用時(shí)應(yīng)該取消 java 語(yǔ)言的訪(fǎng)問(wèn)檢查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//創(chuàng)建這個(gè)類(lèi)的一個(gè)對(duì)象
Object p2 = c2.newInstance();
//將 p2 對(duì)象的 f2 屬性賦值為 Bob,f2 屬性即為 私有屬性 name
f2.set(p2,"Bob");
//使用反射機(jī)制可以打破封裝性,導(dǎo)致了java對(duì)象的屬性不安全。
System.out.println(f2.get(p2)); //Bob
//獲取構(gòu)造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
4、根據(jù)反射獲取父類(lèi)屬性
父類(lèi) Parent.java
public class Parent {
public String publicField = "parent_publicField";
protected String protectField = "parent_protectField";
String defaultField = "parent_defaultField";
private String privateField = "parent_privateField";
}
子類(lèi) Son.java
public class Son extends Parent {
}
public class ReflectionTest {
@Test
public void testGetParentField() throws Exception{
Class c1 = Class.forName("com.ys.model.Son");
//獲取父類(lèi)私有屬性值
System.out.println(getFieldValue(c1.newInstance(),"privateField"));
}
public static Field getDeclaredField(Object obj,String fieldName) {
Field field = null;
Class c = obj.getClass();
for(; c != Object.class ; c = c.getSuperclass()){
try {
field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}catch (Exception e){
//這里甚么都不要做!并且這里的異常必須這樣寫(xiě),不能拋出去。
//如果這里的異常打印或者往外拋,則就不會(huì)執(zhí)行c = c.getSuperclass(),最后就不會(huì)進(jìn)入到父類(lèi)中了
}
}
return null;
}
public static Object getFieldValue(Object object,String fieldName) throws Exception{
Field field = getDeclaredField(object,fieldName);
return field.get(object);
}
}
通過(guò)執(zhí)行上述代碼,我們獲得了父類(lèi)的私有屬性值,這里要注意的是直接通過(guò)反射獲取子類(lèi)的對(duì)象是不能得到父類(lèi)的屬性值的,必須根據(jù)反射獲得的子類(lèi) Class 對(duì)象在調(diào)用 getSuperclass() 方法獲取父類(lèi)對(duì)象,然后在通過(guò)父類(lèi)對(duì)象去獲取父類(lèi)的屬性值。
5、反射總結(jié)
靈活使用反射能讓我們代碼更加靈活,這里比如JDBC原生代碼注冊(cè)驅(qū)動(dòng),hibernate 的實(shí)體類(lèi),Spring 的 AOP等等都有反射的實(shí)現(xiàn)。但是凡事都有兩面性,反射也會(huì)消耗系統(tǒng)的性能,增加復(fù)雜性等,合理使用才是真!
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)與進(jìn)階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
Java+Eclipse+Selenium環(huán)境搭建的方法步驟
這篇文章主要介紹了Java+Eclipse+Selenium環(huán)境搭建的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
Java值得使用Lambda的8個(gè)場(chǎng)景合集
可能對(duì)不少人來(lái)說(shuō),Lambda顯得陌生又復(fù)雜,覺(jué)得Lambda會(huì)導(dǎo)致代碼可讀性下降,但畢竟2023年了,JDK都出了那么多新版本,是時(shí)候試試Lambda了2023-08-08
Spring Boot 開(kāi)發(fā)環(huán)境熱部署詳細(xì)教程
這篇文章主要介紹了Spring Boot 開(kāi)發(fā)環(huán)境熱部署,本文給大家介紹了Spring Boot 開(kāi)發(fā)環(huán)境熱部署的原理及快速配置方法,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot應(yīng)用部署到外置Tomcat的實(shí)現(xiàn)
SpringBoot內(nèi)置tomcat使用很方便,本文主要介紹了SpringBoot應(yīng)用部署到外置Tomcat的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
java?常規(guī)輪詢(xún)長(zhǎng)輪詢(xún)Long?polling實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了java?常規(guī)輪詢(xún)長(zhǎng)輪詢(xún)Long?polling實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
java實(shí)現(xiàn)二維數(shù)組轉(zhuǎn)置的方法示例
這篇文章主要介紹了java實(shí)現(xiàn)二維數(shù)組轉(zhuǎn)置的方法,結(jié)合實(shí)例形式詳細(xì)分析了java二維數(shù)組轉(zhuǎn)置的原理、實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
RestTemplat中關(guān)于getForobject方法的使用
這篇文章主要介紹了RestTemplat中關(guān)于getForobject方法的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
java字節(jié)碼框架ASM的深入學(xué)習(xí)
這篇文章主要給大家介紹了java中字節(jié)碼框架ASM的相關(guān)資料,文中介紹的非常詳細(xì),相信對(duì)大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-01-01
intellij idea 2021.2 打包并上傳運(yùn)行spring boot項(xiàng)目的詳細(xì)過(guò)程(spring boot 2
這篇文章主要介紹了intellij idea 2021.2 打包并上傳運(yùn)行一個(gè)spring boot項(xiàng)目(spring boot 2.5.4),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09

