1 反射機(jī)制簡介
Java的反射(reflection)機(jī)制是指在程序的運(yùn)行狀態(tài)中,可以構(gòu)造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調(diào)用任意一個對象的屬性和方法。這種動態(tài)獲取程序信息以及動態(tài)調(diào)用對象的功能稱為Java語言的反射機(jī)制。反射被視為動態(tài)語言的關(guān)鍵。
反射這一概念最早由編程開發(fā)人員Smith在1982年提出,主要指應(yīng)用程序訪問、檢測、修改自身狀態(tài)與行為的能力。這一概念的提出立刻吸引了編程界的極大關(guān)注,各種研究工作隨之展開,隨之而來引發(fā)編程革命,出現(xiàn)了多種支持反射機(jī)制的面向?qū)ο笳Z言。
在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指一類能夠自我描述和自控制的應(yīng)用。在Java編程語言中,反射是一種強(qiáng)有力的工具,是面向抽象編程一種實(shí)現(xiàn)方式,它能使代碼語句更加靈活,極大提高代碼的運(yùn)行時裝配能力。
2 Class類
對于一個字節(jié)碼文件.class,雖然表面上我們對該字節(jié)碼文件一無所知,但該文件本身卻記錄了許多信息。Java在將.class字節(jié)碼文件載入時,JVM將產(chǎn)生一個java.lang.Class對象代表該.class字節(jié)碼文件,從該Class對象中可以獲得類的許多基本信息,這就是反射機(jī)制。所以要想完成反射操作,就必須首先認(rèn)識Class類。 [1]
反射機(jī)制所需的類主要有java.lang包中的Class類和java.lang.reflet包中的Constructor類、Field類、Method類和Parameter類。Class類是一個比較特殊的類,它是反射機(jī)制的基礎(chǔ),Class類的對象表示正在運(yùn)行的Java程序中的類或接口,也就是任何一個類被加載時,即將類的.class文件(字節(jié)碼文件)讀入內(nèi)存的同時,都自動為之創(chuàng)建一個java.lang.Class對象。Class類沒有公共構(gòu)造方法,其對象是JVM在加載類時通過調(diào)用類加載器中的defineClass()方法創(chuàng)建的,因此不能顯式地創(chuàng)建一個Class對象。通過這個Class對象,才可以獲得該對象的其他信息
3 反射思維導(dǎo)圖

4 反射的具體體現(xiàn)
4.1 準(zhǔn)備工作
1、新建@InheritedAnnotation注解
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedAnnotation {
String value();
}
2、新建@MyAnnotation注解
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
3、新建一個Person類
@InheritedAnnotation("Inherited")
public class Person {
private Integer id;
public Person() {
}
public void say() {
System.out.println("Hello Person");
}
}
4、新建一個User類繼承Person類
@MyAnnotation("myAnnotation")
public class User extends Person {
@MyAnnotation("name")
public String name;
@MyAnnotation("sex")
private String sex;
protected int age;
public User() {
}
@MyAnnotation("User(name, sex, age)")
public User(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
@MyAnnotation("name")
private User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
private void sayHello() {
System.out.println("Hello World");
}
}
4.2 獲取Class類的三種方式
/**
* 三種獲取Class對象的方式
* @param obj
* @return
* @throws ClassNotFoundException
*/
public static Class getClazz(Object obj) throws ClassNotFoundException {
//第一種方式:通過對象的全路徑名獲取Class對象
Class clazz = Class.forName("com.base.domain.User");
System.out.println("1、通過對象的路徑獲取Class對象:" + clazz);
//第二種方式:通過對象的getClass方法獲取Class對象
Class clazz1 = obj.getClass();
System.out.println("2、通過對象實(shí)例的getClass方法獲取Class對象:" + clazz1);
//第三種方式:通過對象的.class方法獲取Class對象
Class clazz2 = User.class;
System.out.println("3、通過對象.class方法獲取Class對象:" + clazz2);
return clazz;
}
執(zhí)行:
public class ReflectTest {
public static void main(String[] args) throws Exception {
User user = new User();
Class clazz = ReflectUtils.getClazz(user);
}
}
執(zhí)行結(jié)果:
1、通過對象的路徑獲取Class對象:class com.base.domain.User
2、通過對象實(shí)例的getClass方法獲取Class對象:class com.base.domain.User
3、通過對象.class方法獲取Class對象:class com.base.domain.User
4.3 獲取類的所有屬性
/**
* 獲取類的所有屬性
* @param clazz
* @throws NoSuchFieldException
*/
public static void getAllFields(Class clazz) throws NoSuchFieldException {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("獲取類中的所有屬性:" + field);
}
Field field = clazz.getDeclaredField("name");
System.out.println("通過屬性名獲取類的屬性:" + field);
}
執(zhí)行結(jié)果:
獲取類中的所有屬性:public java.lang.String com.base.domain.User.name
獲取類中的所有屬性:private java.lang.String com.base.domain.User.sex
獲取類中的所有屬性:protected int com.base.domain.User.age
通過屬性名獲取類的屬性:public java.lang.String com.base.domain.User.name
4.4 獲取類的公開屬性
/**
* 獲取類中的公開屬性
* @param clazz
* @throws NoSuchFieldException
*/
public static void getPublicFields(Class clazz) throws NoSuchFieldException {
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println("獲取類中的共開屬性:" + field);
}
//clazz.getField(String name)方式不能獲取私有的屬性,會拋出NoSuchFieldException異常
Field field1 = clazz.getField("name");
System.out.println("通過屬性名獲取類的公開屬性:" + field1);
}
執(zhí)行結(jié)果:
獲取類中的共開屬性:public java.lang.String com.base.domain.User.name
通過屬性名獲取類的公開屬性:public java.lang.String com.base.domain.User.name
4.5 獲取本類的所有方法
/**
* 獲取本類中的所有方法
* @param clazz
*/
public static void getAllMethods(Class clazz) throws NoSuchMethodException {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("獲取本類的所有方法:" + method);
}
Method method = clazz.getDeclaredMethod("setSex", String.class);
System.out.println("通過方法名獲取setSex方法" + method);
}
執(zhí)行結(jié)果:
獲取本類的所有方法:public java.lang.String com.base.domain.User.toString()
獲取本類的所有方法:public java.lang.String com.base.domain.User.getName()
獲取本類的所有方法:public void com.base.domain.User.setName(java.lang.String)
獲取本類的所有方法:public java.lang.String com.base.domain.User.getSex()
獲取本類的所有方法:public void com.base.domain.User.setSex(java.lang.String)
獲取本類的所有方法:public int com.base.domain.User.getAge()
獲取本類的所有方法:public void com.base.domain.User.setAge(int)
獲取本類的所有方法:private void com.base.domain.User.sayHello()
通過方法名獲取setSex方法public void com.base.domain.User.setSex(java.lang.String)
4.6 獲取本類及父類的公開方法
/**
* 獲取本類及父類的所有公開方法,私有方法不會被獲取
* @param clazz
*/
public static void getPublicMethods(Class clazz) throws NoSuchMethodException {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println("獲取類的所有公開方法,包括父類的方法:" + method);
}
Method method = clazz.getMethod("getSex");
System.out.println("通過方法名獲取getSex方法" + method);
}
執(zhí)行結(jié)果:
獲取類的所有公開方法,包括父類的方法:public java.lang.String com.base.domain.User.toString()
獲取類的所有公開方法,包括父類的方法:public java.lang.String com.base.domain.User.getName()
獲取類的所有公開方法,包括父類的方法:public void com.base.domain.User.setName(java.lang.String)
獲取類的所有公開方法,包括父類的方法:public java.lang.String com.base.domain.User.getSex()
獲取類的所有公開方法,包括父類的方法:public void com.base.domain.User.setSex(java.lang.String)
獲取類的所有公開方法,包括父類的方法:public int com.base.domain.User.getAge()
獲取類的所有公開方法,包括父類的方法:public void com.base.domain.User.setAge(int)
獲取類的所有公開方法,包括父類的方法:public void com.base.domain.Person.say()
獲取類的所有公開方法,包括父類的方法:public final void java.lang.Object.wait() throws java.lang.InterruptedException
獲取類的所有公開方法,包括父類的方法:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
獲取類的所有公開方法,包括父類的方法:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
獲取類的所有公開方法,包括父類的方法:public boolean java.lang.Object.equals(java.lang.Object)
獲取類的所有公開方法,包括父類的方法:public native int java.lang.Object.hashCode()
獲取類的所有公開方法,包括父類的方法:public final native java.lang.Class java.lang.Object.getClass()
獲取類的所有公開方法,包括父類的方法:public final native void java.lang.Object.notify()
獲取類的所有公開方法,包括父類的方法:public final native void java.lang.Object.notifyAll()
通過方法名獲取getSex方法public java.lang.String com.base.domain.User.getSex()
4.7 獲取類的所有構(gòu)造方法
**
* 獲取類的所有構(gòu)造方法
* @param clazz
*/
public static void getAllConstructors(Class clazz) throws NoSuchMethodException {
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("獲取類的所有構(gòu)造方法:" + constructor);
}
Constructor constructor = clazz.getDeclaredConstructor(String.class);
System.out.println("通過參數(shù)個數(shù)及類型獲取構(gòu)造方法:" + constructor);
}
執(zhí)行結(jié)果:
獲取類的所有構(gòu)造方法:private com.base.domain.User(java.lang.String)
獲取類的所有構(gòu)造方法:public com.base.domain.User(java.lang.String,java.lang.String,int)
獲取類的所有構(gòu)造方法:public com.base.domain.User()
通過參數(shù)個數(shù)及類型獲取構(gòu)造方法:private com.base.domain.User(java.lang.String)
4.8 獲取類的公開構(gòu)造方法
/**
* 獲取類的公開構(gòu)造方法
* @param clazz
*/
public static void getPublicConstructors(Class clazz) throws NoSuchMethodException {
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("獲取類的公開構(gòu)造方法:" + constructor);
}
Constructor constructor = clazz.getConstructor(String.class, String.class, int.class);
System.out.println("通過參數(shù)個數(shù)及類型獲取公開的構(gòu)造方法:" + constructor);
}
執(zhí)行結(jié)果:
獲取類的公開構(gòu)造方法:public com.base.domain.User(java.lang.String,java.lang.String,int)
獲取類的公開構(gòu)造方法:public com.base.domain.User()
通過參數(shù)個數(shù)及類型獲取公開的構(gòu)造方法:public com.base.domain.User(java.lang.String,java.lang.String,int)
4.9 獲取本類上的注解
/**
* 獲取類上的注解
* @param clazz
*/
public static void getClassAnnotations(Class clazz) {
Annotation[] annotations = clazz.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println("獲取類上的注解" + annotation);
}
Annotation annotation = clazz.getDeclaredAnnotation(MyAnnotation.class);
System.out.println("通過注解類獲取注解:" + annotation);
}
執(zhí)行結(jié)果:
獲取類上的注解@com.base.annotation.MyAnnotation(value=myAnnotation)
通過注解類獲取注解:@com.base.annotation.MyAnnotation(value=myAnnotation)
4.10 獲取本類及父類的注解
/**
* 獲取類上的注解,會將父類中用@Inherited修飾的注解獲取到
* @param clazz
*/
public static void getAnnotations(Class clazz) {
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("獲取父類中可繼承的及本類中的類注解" + annotation);
}
Annotation annotation = clazz.getAnnotation(InheritedAnnotation.class);
System.out.println("通過注解類獲取注解:" + annotation);
}
執(zhí)行結(jié)果:
獲取父類中可繼承的及本類中的類注解@com.base.annotation.InheritedAnnotation(value=Inherited)
獲取父類中可繼承的及本類中的類注解@com.base.annotation.MyAnnotation(value=myAnnotation)
通過注解類獲取注解:@com.base.annotation.InheritedAnnotation(value=Inherited)
4.11 通過反射實(shí)例化對象
/**
* 通過反射實(shí)例化對象
* @param clazz
* @return
* @throws Exception
*/
public static Object newInstanceObject(Class clazz) throws Exception {
User user = (User)clazz.newInstance();
System.out.println("通過newInstance方式實(shí)例化對象:" + user.toString());
Constructor constructor = clazz.getDeclaredConstructor(String.class, String.class, int.class);
user = (User) constructor.newInstance("張三", "男", 18);
System.out.println("通過公開的構(gòu)造方法方式實(shí)例化對象:" + user.toString());
constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);//訪問私有的方法及屬性需要將Accessible設(shè)置為true
user = (User) constructor.newInstance("李四");
System.out.println("通過私有的構(gòu)造方法方式實(shí)例化對象:" + user.toString());
return user;
}
執(zhí)行結(jié)果:
通過newInstance方式實(shí)例化對象:User{name='null', sex='null', age=0}
通過公開的構(gòu)造方法方式實(shí)例化對象:User{name='張三', sex='男', age=18}
通過私有的構(gòu)造方法方式實(shí)例化對象:User{name='李四', sex='null', age=0}
4.12 通過反射調(diào)用方法
/**
* 通過反射調(diào)用方法
* @param clazz
* @throws Exception
*/
public static void invokeMethod(Class clazz) throws Exception {
Method method = clazz.getDeclaredMethod("setAge", int.class);
User user = (User) newInstanceObject(clazz);
method.invoke(user, 28);
System.out.println("通過反射調(diào)用setAge方法:" + user.toString());
}
執(zhí)行結(jié)果:
通過反射調(diào)用setAge方法:User{name='李四', sex='null', age=28}
4.13 通過反射修改屬性
/**
* 通過反射修改屬性值
* @param clazz
* @throws Exception
*/
public static void invokeUpdateField(Class clazz) throws Exception {
Field field = clazz.getDeclaredField("sex");
User user = (User) newInstanceObject(clazz);
field.setAccessible(true);//如果是公開的屬性,則不需要setAccessible為true
field.set(user, "女");
System.out.println("通過反射修改對象的私有屬性值:" + user.toString());
}
執(zhí)行結(jié)果:
通過反射修改對象的私有屬性值:User{name='李四', sex='女', age=0}
5 反射意義
首先,反射機(jī)制極大的提高了程序的靈活性和擴(kuò)展性,降低模塊的耦合性,提高自身的適應(yīng)能力。
其次,通過反射機(jī)制可以讓程序創(chuàng)建和控制任何類的對象,無需提前硬編碼目標(biāo)類。
再次,使用反射機(jī)制能夠在運(yùn)行時構(gòu)造一個類的對象、判斷一個類所具有的成員變量和方法、調(diào)用一個對象的方法。
最后,反射機(jī)制是構(gòu)建框架技術(shù)的基礎(chǔ)所在,使用反射可以避免將代碼寫死在框架中。
正是反射有以上的特征,所以它能動態(tài)編譯和創(chuàng)建對象,極大的激發(fā)了編程語言的靈活性,強(qiáng)化了多態(tài)的特性,進(jìn)一步提升了面向?qū)ο缶幊痰某橄竽芰Γ蚨艿骄幊探绲那嗖A。
6 反射特點(diǎn)
盡管反射機(jī)制帶來了極大的靈活性及方便性,但反射也有缺點(diǎn)。反射機(jī)制的功能非常強(qiáng)大,但不能濫用。在能不使用反射完成時,盡量不要使用,原因有以下幾點(diǎn):
1、性能問題。
Java反射機(jī)制中包含了一些動態(tài)類型,所以Java虛擬機(jī)不能夠?qū)@些動態(tài)代碼進(jìn)行優(yōu)化。因此,反射操作的效率要比正常操作效率低很多。我們應(yīng)該避免在對性能要求很高的程序或經(jīng)常被執(zhí)行的代碼中使用反射。而且,如何使用反射決定了性能的高低。如果它作為程序中較少運(yùn)行的部分,性能將不會成為一個問題。
2、安全限制。
使用反射通常需要程序的運(yùn)行沒有安全方面的限制。如果一個程序?qū)Π踩蕴岢鲆?,則最好不要使用反射。
3、程序健壯性。
反射允許代碼執(zhí)行一些通常不被允許的操作,所以使用反射有可能會導(dǎo)致意想不到的后果。反射代碼破壞了Java程序結(jié)構(gòu)的抽象性,所以當(dāng)程序運(yùn)行的平臺發(fā)生變化的時候,由于抽象的邏輯結(jié)構(gòu)不能被識別,代碼產(chǎn)生的效果與之前會產(chǎn)生差異。
7 總結(jié)
1、Calss.getAnnotations();方法獲取注解,父類的注解必須含有元注解@Inherited才能被獲??;
2、通過私有的構(gòu)造函數(shù)實(shí)例化對象或者修改私有屬性時需要設(shè)置setAccessible為true,才能正常執(zhí)行;
3、本文列舉了反射機(jī)制使用過程中常用的、重要的一些類及其方法,其他用法請自行查看官方文檔。
|