概述
定義
Java反射機(jī)制是在運(yùn)行狀態(tài)中笆制,對(duì)于任意一個(gè)類叫搁,都能夠知道這個(gè)類的所有屬性和方法匈仗;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性悍手。這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為Java語(yǔ)言的反射機(jī)制帘睦。
用途
通過反射,Java 代碼可以發(fā)現(xiàn)有關(guān)已加載類的字段坦康,方法和構(gòu)造函數(shù)的信息竣付,并可以在安全限制內(nèi)對(duì)這些字段,方法和構(gòu)造函數(shù)進(jìn)行操作滞欠。
很多人都認(rèn)為反射在實(shí)際Java中開發(fā)應(yīng)用中并不廣泛卑笨,其實(shí)不然。當(dāng)我們?cè)谑褂?IDE(如 IDEA/Eclipse)時(shí)仑撞,當(dāng)我們輸入一個(gè)對(duì)象或者類并調(diào)用它的屬性和方法時(shí)赤兴,一按 (“.”)點(diǎn)號(hào),編譯器就會(huì)自動(dòng)列出她的屬性或方法隧哮,這里就會(huì)用到反射桶良。
反射最重要的用途就是開發(fā)各種通用框架
很多框架(比如 Spring)都是配置化的(比如Spring 通過 XML 配置模式裝載 Bean),為了保證框架的通用性沮翔,他們可能根據(jù)配置文件加載不同的對(duì)象或類陨帆,調(diào)用不同的方法,這個(gè)時(shí)候就必須用到反射——運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象采蚀。
對(duì)于框架開發(fā)人員來(lái)說(shuō)疲牵,反射作用非常大,它是各種容器實(shí)現(xiàn)的核心榆鼠。而對(duì)于一般的開發(fā)者來(lái)說(shuō)挤庇,不深入框架開發(fā)反射用的就會(huì)少一點(diǎn)群井,不過了解一下框架的底層機(jī)制有助于豐富自己的編程思想锌俱,也是很有益的迎膜。
Java反射框架提供一下功能:
- 在運(yùn)行時(shí)判定任意一個(gè)對(duì)象所屬的類
- 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
- 在運(yùn)行時(shí)判定任意一個(gè)類所具有的成員變量和方法
- 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法
反射的優(yōu)缺點(diǎn)
反射的優(yōu)點(diǎn)
使用反射機(jī)制,代碼可以在運(yùn)行時(shí)裝配神妹,提高了程序的靈活性和擴(kuò)展性颓哮,降低耦合性,提高自適應(yīng)能力鸵荠。它允許程序創(chuàng)建和控制任何類的對(duì)象冕茅,無(wú)需硬編碼目標(biāo)類
反射的缺點(diǎn)
性能問題:使用反射基本上是一種解釋操作,JVM無(wú)法對(duì)這些代碼進(jìn)行優(yōu)化,因此姨伤,反射操作的效率要比那些非反射操作低得多哨坪。反射機(jī)制主要應(yīng)用在對(duì)靈活性和擴(kuò)展性要求很高的系統(tǒng)框架上,對(duì)性能要求高的程序中不建議使用
安全限制:使用反射技術(shù)要求程序必須在一個(gè)沒有安全限制的環(huán)境中運(yùn)行
內(nèi)部暴露:由于反射允許代碼執(zhí)行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方法)姜挺,所以使用反射可能會(huì)導(dǎo)致意料之外的副作用——代碼有功能上的錯(cuò)誤齿税,降低可移植性彼硫。反射代碼破壞了抽象性炊豪,因此當(dāng)平臺(tái)發(fā)生改變的時(shí)候,代碼的行為就有可能也隨著變化拧篮。
反射機(jī)制的相關(guān)類
與Java反射相關(guān)的類如下:
類名 | 用途 |
---|---|
Class類 | 代表類的實(shí)體词渤,在運(yùn)行的Java應(yīng)用程序中表示類和接口 |
Field類 | 代表類的成員變量(成員變量也稱為類的屬性) |
Method類 | 代表類的方法 |
Constructor類 | 代表類的構(gòu)造方法 |
Class類
Class代表類的實(shí)體,在運(yùn)行的Java應(yīng)用程序中表示類和接口串绩。在這個(gè)類中提供了很多有用的方法缺虐,這里對(duì)他們簡(jiǎn)單的分類介紹。
- 獲得類相關(guān)的方法
方法 | 用途 |
---|---|
asSubclass(Class<U> clazz) | 把傳遞的類的對(duì)象轉(zhuǎn)換成代表其子類的對(duì)象 |
Cast | 把對(duì)象轉(zhuǎn)換成代表類或是接口的對(duì)象 |
getClassLoader() | 獲得類的加載器 |
getClasses() | 返回一個(gè)數(shù)組礁凡,數(shù)組中包含該類中所有公共類和接口類的對(duì)象 |
getDeclaredClasses() | 返回一個(gè)數(shù)組高氮,數(shù)組中包含該類中所有類和接口類的對(duì)象 |
forName(String className) | 根據(jù)類名返回類的對(duì)象 |
getName() | 獲得類的完整路徑名字 |
newInstance() | 創(chuàng)建類的實(shí)例 |
getPackage() | 獲得類的包 |
getSimpleName() | 獲得類的名字 |
getSuperclass() | 獲得當(dāng)前類繼承的父類的名字 |
getInterfaces() | 獲得當(dāng)前類實(shí)現(xiàn)的類或是接口 |
- 獲得類中屬性相關(guān)的方法
方法 | 用途 |
---|---|
getField(String name) | 獲得某個(gè)公有的屬性對(duì)象 |
getFields() | 獲得所有公有的屬性對(duì)象 |
getDeclaredField(String name) | 獲得某個(gè)屬性對(duì)象 |
getDeclaredFields() | 獲得所有屬性對(duì)象 |
- 獲得類中注解相關(guān)的方法
方法 | 用途 |
---|---|
getAnnotation(Class<A> annotationClass) | 返回該類中與參數(shù)類型匹配的公有注解對(duì)象 |
getAnnotations() | 返回該類所有的公有注解對(duì)象 |
getDeclaredAnnotation(Class<A> annotationClass) | 返回該類中與參數(shù)類型匹配的所有注解對(duì)象 |
getDeclaredAnnotations() | 返回該類所有的注解對(duì)象 |
- 獲得類中構(gòu)造器相關(guān)的方法
方法 | 用途 |
---|---|
getConstructor(Class...<?> parameterTypes) | 獲得該類中與參數(shù)類型匹配的公有構(gòu)造方法 |
getConstructors() | 獲得該類的所有公有構(gòu)造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 獲得該類中與參數(shù)類型匹配的構(gòu)造方法 |
getDeclaredConstructors() | 獲得該類所有構(gòu)造方法 |
- 獲得類中方法相關(guān)的方法
方法 | 用途 |
---|---|
getMethod(String name, Class...<?> parameterTypes) | 獲得該類某個(gè)公有的方法 |
getMethods() | 獲得該類所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) | 獲得該類某個(gè)方法 |
getDeclaredMethods() | 獲得該類所有方法 |
- 類中其他重要的方法
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解類型則返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定類型注解類型則返回true |
isAnonymousClass() | 如果是匿名類則返回true |
isArray() | 如果是一個(gè)數(shù)組類則返回true |
isEnum() | 如果是枚舉類則返回true |
isInstance(Object obj) | 如果obj是該類的實(shí)例則返回true |
isInterface() | 如果是接口類則返回true |
isLocalClass() | 如果是局部類則返回true |
isMemberClass() | 如果是內(nèi)部類則返回true |
Field類
Field代表類的成員變量(成員變量也稱為類的屬性)。
方法 | 用途 |
---|---|
equals(Object obj) | 屬性與obj相等則返回true |
get(Object obj) | 獲得obj中對(duì)應(yīng)的屬性值 |
set(Object obj, Object value) | 設(shè)置obj中對(duì)應(yīng)屬性值 |
Method類
Method代表類的方法顷牌。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) | 傳遞object對(duì)象及參數(shù)調(diào)用該對(duì)象對(duì)應(yīng)的方法 |
Constructor類
Constructor代表類的構(gòu)造方法剪芍。
方法 | 用途 |
---|---|
newInstance(Object... initargs) | 根據(jù)傳遞的參數(shù)創(chuàng)建類的對(duì)象 |
示例
為了演示反射的使用,首先構(gòu)造一個(gè)與書籍相關(guān)的model——Book.java窟蓝,然后了解獲取Class類對(duì)象的三種方法罪裹,最后通過反射方法示例創(chuàng)建對(duì)象、反射私有構(gòu)造方法运挫、反射私有屬性状共、反射私有方法
被反射類Book.java
public class Book{
private final static String TAG = "BookTag";
private String name;
private String author;
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
public Book() {
}
private Book(String name, String author) {
this.name = name;
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
private String declaredMethod(int index) {
String string = null;
switch (index) {
case 0:
string = "I am declaredMethod 0 !";
break;
case 1:
string = "I am declaredMethod 1 !";
break;
default:
string = "I am declaredMethod -1 !";
}
return string;
}
}
獲取Class類對(duì)象的三種方法
在反射中,要獲取一個(gè)類或調(diào)用一個(gè)類的方法谁帕,我們首先需要獲取到該類的 Class 對(duì)象峡继。
在 Java API 中,獲取 Class 類對(duì)象的三種方法
方法一:使用 Class.forName 靜態(tài)方法匈挖。當(dāng)你知道該類的全路徑名時(shí)鬓椭,你可以使用該方法獲取 Class 類對(duì)象。但可能拋出 ClassNotFoundException 異常
Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
方法二:這種方法只適合在編譯前就知道操作的 Class关划。直接通過 類名.class 的方式得到小染,該方法最為安全可靠,程序性能更高贮折,這說(shuō)明任何一個(gè)類都有一個(gè)隱含的靜態(tài)成員變量 class
Class clz = Book.class;
方法三:使用類對(duì)象的 getClass() 方法裤翩。
Book book1 = new Book();
Class<? extends Book> book1Class = book1.getClass();
@Test
public void getClz() {
try {
// 方法一,使用 Class.forName 靜態(tài)方法
Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
// 方法二:直接通過 類名.class 的方式得到
Class clz = Book.class;
// 方法三:使用類對(duì)象的 getClass() 方法。
Book book1 = new Book();
Class<? extends Book> book1Class = book1.getClass();
System.out.println(classBook == clz);
System.out.println(classBook == book1Class);
} catch (Exception ex) {
ex.printStackTrace();
}
}
輸出結(jié)果都是true踊赠,可以知道三種方法獲取到的Class對(duì)象都是同一個(gè)對(duì)象
true
true
反射常用類和方法測(cè)試
public class ReflectClass {
private static final Logger log = LoggerFactory.getLogger(ReflectClass.class);
// 創(chuàng)建對(duì)象
@Test
public void reflectNewInstance() {
try {
Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
Object objectBook = classBook.newInstance();
Book book = (Book) objectBook;
book.setName("Java高級(jí)特性-反射-創(chuàng)建對(duì)象");
book.setAuthor("JourWon");
log.info(book.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 反射私有的構(gòu)造方法
@Test
public void reflectPrivateConstructor() {
try {
Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
Constructor<?> declaredConstructorBook = classBook.getDeclaredConstructor(String.class, String.class);
// 暴力反射
declaredConstructorBook.setAccessible(true);
Object objectBook = declaredConstructorBook.newInstance("Java高級(jí)特性-反射-反射私有的構(gòu)造方法", "JourWon");
Book book = (Book) objectBook;
log.info(book.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 反射私有屬性
@Test
public void reflectPrivateField() {
try {
Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
Object objectBook = classBook.newInstance();
Field fieldTag = classBook.getDeclaredField("TAG");
fieldTag.setAccessible(true);
String tag = (String) fieldTag.get(objectBook);
log.info(tag);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 反射私有方法
@Test
public void reflectPrivateMethod() {
try {
Class<?> classBook = Class.forName("com.jourwon.reflect.Book");
Method methodBook = classBook.getDeclaredMethod("declaredMethod", int.class);
methodBook.setAccessible(true);
Object objectBook = classBook.newInstance();
String string = (String) methodBook.invoke(objectBook, 0);
ReflectClass.
log.info(string);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
輸出結(jié)果
[main] INFO com.jourwon.reflect.ReflectClass - Book{name='Java高級(jí)特性-反射-創(chuàng)建對(duì)象', author='JourWon'}
[main] INFO com.jourwon.reflect.ReflectClass - Book{name='Java高級(jí)特性-反射-反射私有的構(gòu)造方法', author='JourWon'}
[main] INFO com.jourwon.reflect.ReflectClass - BookTag
[main] INFO com.jourwon.reflect.ReflectClass - I am declaredMethod 0 !
通過反射獲取私有屬性呵扛,方法和構(gòu)造方法時(shí),需要進(jìn)行暴力反射筐带,設(shè)置setAccessible(true)今穿。否則會(huì)報(bào)錯(cuò)說(shuō)無(wú)法獲取私有屬性,方法和構(gòu)造方法
總結(jié)
本文列舉了反射機(jī)制使用過程中常用的伦籍、重要的一些類及其方法蓝晒,更多信息和用法,反射原理和源碼分析需要近一步的閱讀反射相關(guān)資料帖鸦。
在閱讀Class類文檔時(shí)發(fā)現(xiàn)一個(gè)特點(diǎn)芝薇,以通過反射獲得Method對(duì)象為例,一般會(huì)提供四種方法作儿,getMethod(parameterTypes)洛二、getMethods()、getDeclaredMethod(parameterTypes)和getDeclaredMethods()攻锰。getMethod(parameterTypes)用來(lái)獲取某個(gè)公有的方法的對(duì)象晾嘶,getMethods()獲得該類所有公有的方法,getDeclaredMethod(parameterTypes)獲得該類某個(gè)方法娶吞,getDeclaredMethods()獲得該類所有方法垒迂。帶有Declared修飾的方法可以反射到私有的方法,沒有Declared修飾的只能用來(lái)反射公有的方法寝志。其他的Annotation娇斑、Field、Constructor也是如此材部。