一、Java反射的理解(反射是研究框架的基础之一)
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
二、逐步分析
1、关于Class
1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
4、Class 对象只能由系统建立对象
5、一个类在 JVM 中只会有一个Class实例
下面创建一个例子:后面的分析均以此案例为基准。
创建一个空接口:(后面会不断补充)
1 packagecom.xfwl.reflection;2
3 public interfaceIHuman {4
5 }
创建一个空基类:(后面会不断补充)
1 packagecom.xfwl.reflection;2
3 public classHuman {4
5 }
创建一个子类:(后面会不断补充)
1 packagecom.xfwl.reflection;2
3 public class Person extends Human implementsIHuman {4 /**
5 * 默认default修饰6 */
7 String name;8 /**
9 * private修饰10 */
11 private intage;12 /**
13 * public修饰14 */
15 public char sex='M';16 /**
17 * 无参构造18 */
19 publicPerson(){20 System.out.println("无参构造!!!");21 }22 /**
23 * 有参构造24 */
25 public Person(String name,int age,charsex){26 System.out.println("有参构造!!!");27 this.name=name;28 this.age=age;29 this.sex=sex;30 }31 publicString getName() {32 returnname;33 }34 public voidsetName(String name) {35 this.name =name;36 }37 public intgetAge() {38 returnage;39 }40 public void setAge(intage) {41 this.age =age;42 }43 public chargetSex() {44 returnsex;45 }46 public void setSex(charsex) {47 this.sex =sex;48 }49 publicString toString() {50 return "Person{" +
51 "name='" + name + '\'' +
52 ", age=" + age +
53 ", sex='" + sex + '\'' +
54 '}';55 }56 }
2、反射获取类对象的三种方式(通过一个Junit测试来说明)
1 packagecom.xfwl.reflection;2
3 importorg.junit.Test;4 /**
5 * 测试类6 * @function7 *@author小风微凉8 * @time -6-3 下午12:28:389 */
10 public classTestAction {11 /**
12 * 反射机制获取类有三种方法13 */
14 @Test15 public void testGetClass() throwsClassNotFoundException {16 Class clazz = null;17
18 //1 直接通过类名.Class的方式得到
19 clazz = Person.class;20 System.out.println("通过类名: " +clazz);21
22 //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)
23 Object obj = newPerson();24 clazz =obj.getClass();25 System.out.println("通过getClass(): " +clazz);26
27 //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
28 clazz = Class.forName("com.xfwl.reflection.Person");29 System.out.println("通过全类名获取: " +clazz);30 }31 }
运行结果:
通过类名: classcom.xfwl.reflection.Person
无参构造!!!
通过getClass():classcom.xfwl.reflection.Person
通过全类名获取:class com.xfwl.reflection.Person
特别注意:(以下2中方式不会调用构造方法,因为没有实例化操作)
//1 直接通过类名.Class的方式得到
clazz = Person.class;//3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
clazz = Class.forName("com.xfwl.reflection.Person");
3、利用newInstance创建对象:调用的类必须有无参的构造器
1 /**
2 * Class类的newInstance()方法,创建类的一个对象。3 *@throwsClassNotFoundException4 *@throwsIllegalAccessException5 *@throwsInstantiationException6 */
7 @Test8 public voidtestNewInstance()9 throwsClassNotFoundException, IllegalAccessException, InstantiationException {10
11 Class clazz = Class.forName("com.xfwl.reflection.Person");12
13 //使用Class类的newInstance()方法创建类的一个对象14 //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)15 //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器
16 Object obj =clazz.newInstance();17 System.out.println(obj);18 }
测试结果:
那么,如果删除Person.java中的无参构造,继续测试,结果如下:
4、ClassLoader类加载器
1 /**
2 * ClassLoader类装载器3 */
4 @Test5 public void testClassLoader1() throwsClassNotFoundException, IOException {6 //1、获取一个系统的类加载器
7 ClassLoader classLoader =ClassLoader.getSystemClassLoader();8 System.out.println("系统的类加载器-->" +classLoader);9
10 //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))
11 classLoader =classLoader.getParent();12 System.out.println("扩展类加载器-->" +classLoader);13
14 //3、获取扩展类加载器的父类加载器15 //输出为Null,无法被Java程序直接引用
16 classLoader =classLoader.getParent();17 System.out.println("启动类加载器-->" +classLoader);18
19 //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器
20 classLoader = Class.forName("com.xfwl.reflection.Person").getClassLoader();21 System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);22
23 //5、测试JDK提供的Object类由哪个类加载器负责加载的24 //输出为Null,无法被Java程序直接引用
25 classLoader = Class.forName("java.lang.Object").getClassLoader();26 System.out.println("JDK提供的Object类由哪个类加载器加载-->" +classLoader);27 }
测试结果:
系统的类加载器-->sun.misc.Launcher$AppClassLoader@18b4aac2
扩展类加载器-->sun.misc.Launcher$ExtClassLoader@614c5515
启动类加载器-->null当前类由哪个类加载器进行加载-->sun.misc.Launcher$AppClassLoader@18b4aac2
JDK提供的Object类由哪个类加载器加载-->null
5、反射机制通过加载器获取流对象:getResourceAsStream方法
1 /**
2 * 反射机制通过加载器获取流对象:getResourceAsStream方法3 *@throwsClassNotFoundException4 *@throwsIOException5 */
6 @Test7 public void testGetResourceAsStream() throwsClassNotFoundException, IOException {8 //调用getResourceAsStream 获取类路径下的文件对应的输入流
9 /**
10 * 特别说明:11 * getResourceAsStream("path"),path的路径和new Person()的位置有关12 */
13
14 InputStream in = newPerson().getClass().getClassLoader()15 .getResourceAsStream("com/xfwl/reflection/test.properties");16 System.out.println("in: " +in);17
18 Properties properties = newProperties();19 properties.load(in);20 System.out.println("文件内容:"+properties);21 System.out.println("name: "+properties.getProperty("name"));22 System.out.println("age: " + properties.getProperty("age"));23 System.out.println("sex: "+properties.getProperty("sex"));24 System.out.println("desc: " + properties.getProperty("desc"));25 }
test.properties文件内容如下:文件编码格式:ISO-8859-1
name=\u5C0F\u98CE\u5FAE\u51C9\u0087\u0089
age=23sex=M
desc=\u53CD\u5C04\u673A\u5236\u5B66\u4E60
运行结果:(直接解析会出现乱码问题,这个可以通过new String(乱码格式处理参数)来处理)
无参构造!!!
in: java.io.BufferedInputStream@215be6bb
文件内容:{age=23, name=小风微凉??, sex=M, desc=反射机制学习}
name: 小风微凉??age:23sex: M
desc: 反射机制学习
6、反射机制获取类中的方法:Method: 对应类中的方法
现在给Person类添加一个private方法、一个public 方法、一个defaut 方法、一个protected方法
1 /**
2 * Java权限有四个,分别为public,protected,默认,private,其开放程度依次降低3 * public可供所有类访问4 * protected继承可见5 * private只能类本身内部的方法可以访问6 */
7 public voidmethod_public(){8 System.out.println("method_public");9 }10 public void method_public_2(String name,int age,char sex){//public 带参数
11 System.out.println("method_public_2");12 String info="Person{" +
13 "name='" + name + '\'' +
14 ", age=" + age +
15 ", sex='" + sex + '\'' +
16 '}';17 System.out.println(info);18 }19 protected voidmethod_protected(){20 System.out.println("method_protected");21 }22 protected void method_protected_2(String info){//protected 带参数
23 System.out.println("method_protected_2:"+info);24 }25 voidmethod_default(){26 System.out.println("method_default");27 }28 void method_default_2(String info){//默认修饰符 带参数
29 System.out.println("method_default_2:"+info);30 }31 private voidmethod_private(){32 System.out.println("method_private");33 }34 private void method_private_2(String info){//private 带参数
35 System.out.println("method_private_2:"+info);36 }
开始测试如何通过反射机制使用这些方法
1 /**
2 * 如何通过反射机制使用这些方法3 *@throwsClassNotFoundException4 *@throwsNoSuchMethodException5 *@throwsIllegalAccessException6 *@throwsInstantiationException7 *@throwsInvocationTargetException8 */
9 @Test10 public void testMethod() throwsClassNotFoundException, NoSuchMethodException,11 IllegalAccessException, InstantiationException, InvocationTargetException {12 Class clazz = Class.forName("com.xfwl.reflection.Person");13
14 //1、得到clazz 对应的类中有哪些方法,不能获取private方法
15 Method[] methods =clazz.getMethods();16 System.out.println("通过反射机制可以拿到的方法:clazz.getMethods()");17 for(Method method : methods){18 System.out.println(method.getName());19 }20 System.out.println("");21
22 //2、获取所有的方法(且只获取当着类声明的方法,包括private方法)
23 Method[] methods2 =clazz.getDeclaredMethods();24 System.out.println("通过反射机制可以拿到的方法:clazz.getDeclaredMethods()");25 for(Method method : methods2){26 System.out.println(method.getName());27 }28 System.out.println("");29 System.out.println("通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()");30 //3、获取指定的方法
31 Method method1= clazz.getDeclaredMethod("method_private");32 System.out.println("private 无参:"+method1);33
34 Method method2 = clazz.getDeclaredMethod("method_private_2",String.class);//第一个参数是方法名,后面的是方法里的参数
35 System.out.println("private 有参:"+method2);36
37 Method method3 = clazz.getDeclaredMethod("method_public_2",String.class,int.class,char.class);//第一个参数是方法名,后面的是方法里的参数
38 System.out.println("public 有参:"+method2);39
40 //4、执行方法!
41 Object obj =clazz.newInstance();42 method3.invoke(obj, "小风微凉", 23,'M'); //执行方法:invoke(类对象)43 }
测试结果:
通过反射机制可以拿到的方法:clazz.getMethods():不能获取private/protected/default方法
toString
getName
setName
method_public_2
setAge
method_public
getSex
getAge
setSex
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll通过反射机制可以拿到的方法:clazz.getDeclaredMethods():获取所有修饰权限的方法
toString
getName
setName
method_private_2
method_private
method_public_2
setAge
method_public
method_default
method_default_2
getSex
getAge
setSex
method_protected
method_protected_2通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()private 无参:private voidcom.xfwl.reflection.Person.method_private()private 有参:private voidcom.xfwl.reflection.Person.method_private_2(java.lang.String)public 有参:private voidcom.xfwl.reflection.Person.method_private_2(java.lang.String)
无参构造!!!
method_public_2
Person{name='小风微凉', age=23, sex='M'}
继续分析一下:
JDK中的获取方法
获取方法:默认只能获取public修饰的方法
1 @CallerSensitive2 public Method[] getMethods() throwsSecurityException {3 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);4 returncopyMethods(privateGetPublicMethods());5 }
获取方法:所有修饰权限的方法都可以获得
@CallerSensitivepublic Method[] getDeclaredMethods() throwsSecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(),true);return copyMethods(privateGetDeclaredMethods(false));
}
获取方法:获取指定的方法(所有修饰权限)
1 /**
2 * @jls 8.2 Class Members3 * @jls 8.4 Method Declarations4 *@sinceJDK1.15 */
6 @CallerSensitive7 public Method getDeclaredMethod(String name, Class>... parameterTypes)8 throwsNoSuchMethodException, SecurityException {9 checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);10 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);11 if (method == null) {12 throw new NoSuchMethodException(getName() + "." + name +argumentTypesToString(parameterTypes));13 }14 returnmethod;15 }
分析一下上面这个方法:
String name:方法的名称
Class>... parameterTypes:一个或多个方法参数的类型,注意要一一对应,否则会报错的哦
执行方法:invoke(方法对象,方法实际参数)
1 @CallerSensitive2 publicObject invoke(Object obj, Object... args)3 throwsIllegalAccessException, IllegalArgumentException,4 InvocationTargetException5 {6 if (!override) {7 if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {8 Class> caller =Reflection.getCallerClass();9 checkAccess(caller, clazz, obj, modifiers);10 }11 }12 MethodAccessor ma = methodAccessor; //read volatile
13 if (ma == null) {14 ma =acquireMethodAccessor();15 }16 returnma.invoke(obj, args);17 }
7、反射机制获取类中的方法:Method: 对应基类或接口中的方法
上面分析了,如何通过反射拿到当前本类里面的各个修饰权限的方法,下面来继续分析一下,如何读取父类或实现的接口中的方法:
现在,给父类添加一些方法,在接口中定义一些方法:
接口中的方法声明:
1 packagecom.xfwl.reflection;2
3 public interfaceIHuman {4
5 voideat();6 voideat(String info);7 }
Person.java实现接口方法
1 public voideat() {2 System.out.println("实现接口的方法:eat()无参:");3 }4 public voideat(String info) {5 System.out.println("实现接口的方法:eat()有参:"+info);6 }
父类中的方法定义:
1 packagecom.xfwl.reflection;2
3 public classHuman {4 public voidplay_public(){5 System.out.println("public无参:play_public");6 }7 public voidplay_public_2(String info){8 System.out.println("public有参:play_public2:"+info);9 }10 protected voidplay_protected(){11 System.out.println("protected无参:play_protected");12 }13 protected voidplay_protected_2(String info){14 System.out.println("protected有参:play_protected_2:"+info);15 }16 voidplay_default(){17 System.out.println("默认修饰符无参:play_default");18 }19 void play_default_2(String info){//默认修饰符 带参数
20 System.out.println("默认修饰符有参:play_default_2:"+info);21 }22 private voidplay_private(){23 System.out.println("private无参:play_private");24 }25 private voidplay_private_2(String info){26 System.out.println("private有参:play_private_2:"+info);27 }28 }
开始测试:
1、拿到当前Person类反射对象,能否得到接口中的方法
1 /**
2 * 反射机制获取类中的方法:Method: 对应基类或接口中的方法3 *@throwsClassNotFoundException4 *@throwsSecurityException5 *@throwsNoSuchMethodException6 *@throwsInstantiationException7 *@throwsInvocationTargetException8 *@throwsIllegalArgumentException9 *@throwsIllegalAccessException10 */
11 @Test12 public void testInterfaceOrSupperClass() throwsClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{13 Class clazz = Class.forName("com.xfwl.reflection.Person");14 //拿到当前Person类反射对象,能否得到接口中的方法
15 for(Method method : clazz.getMethods()){16 System.out.println(method.getName());17 }18 //获取当前类实现的接口中的方法
19 Method method1= clazz.getDeclaredMethod("eat");20 Method method2= clazz.getDeclaredMethod("eat",String.class);21 //执行
22 method1.invoke(clazz.newInstance());23 method2.invoke(clazz.newInstance(),"eat有参数");24 }
测试结果:(可以拿到实现的接口中的方法并执行)
toString
getName
setName
eat
eat
method_public_2
setAge
getSex
setSex
getAge
method_public
play_public_2
play_public
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
无参构造!!!
实现接口的方法:eat()无参:
无参构造!!!
实现接口的方法:eat()有参:eat有参数
2、拿到当前Person类反射对象,能否获取父类中的方法
通过当前反射对象,拿到父类反射对象
1 Class clazz = Class.forName("com.xfwl.reflection.Person");2 Class superClazz = clazz.getSuperclass();
1 /**
2 * 反射机制获取类中的方法:Method: 对应基类或接口中的方法3 *@throwsClassNotFoundException4 *@throwsSecurityException5 *@throwsNoSuchMethodException6 *@throwsInstantiationException7 *@throwsInvocationTargetException8 *@throwsIllegalArgumentException9 *@throwsIllegalAccessException10 */
11 @Test12 public void testInterfaceOrSupperClass() throwsClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{13 Class clazz = Class.forName("com.xfwl.reflection.Person");14 System.out.println("");15 //拿到当前Person类反射对象,能否获取父类中的方法
16 Class superClazz =clazz.getSuperclass();17 for(Method method : superClazz.getMethods()){18 System.out.println(method.getName());19 }20 System.out.println("");21 //拿到父类中的所有权限修饰符修饰的方法
22 for(Method method : superClazz.getDeclaredMethods()){23 System.out.println(method.getName());24 }25 }
运行结果:
play_public
play_public_2
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAllplay_private_2
play_public
play_public_2
play_private
play_protected
play_default
play_protected_2
play_default_2
那么可以执行父类中的方法吗?
1 //是否可以通过子类对象拿到父类中的方法
2 Method method3= clazz.getDeclaredMethod("play_public");3 Method method4= clazz.getDeclaredMethod("play_public_2",String.class);4 Method method5= clazz.getDeclaredMethod("play_private");5 Method method6= clazz.getDeclaredMethod("play_private_2",String.class);
上面代码报错,说明不可以,public修饰的方法也拿不到
1 Method method7= superClazz.getDeclaredMethod("play_public");2 Method method8= superClazz.getDeclaredMethod("play_public_2",String.class);3 Method method9= superClazz.getDeclaredMethod("play_private");4 Method method10= superClazz.getDeclaredMethod("play_private_2",String.class);
上面代码正常执行,说明父类的反射对象可以拿到自己的public或private方法
1 //使用子类的反射对象执行方法
2 method7.invoke(clazz.newInstance());3 method8.invoke(clazz.newInstance(), "play_public_2有参数");4 method9.invoke(clazz.newInstance()); //无法执行,Junit报错
5 method10.invoke(clazz.newInstance(), "play_private_2有参数");//无法执行,Junit报错
1 //使用父类的反射对象执行方法
2 method7.invoke(superClazz.newInstance());3 method8.invoke(superClazz.newInstance(), "play_public_2有参数");4 method9.invoke(superClazz.newInstance()); //无法执行,Junit报错
5 method10.invoke(superClazz.newInstance(), "play_private_2有参数");//无法执行,Junit报错
上面代码执行部分报错,说明通过子类的反射对象和拿到的父类反射对象,也仅仅只能执行public和protected和default默认修饰的方法,不能执行private修饰的方法
7、反射机制获取类中的字段属性:Field字段
1 /**
2 * 默认default修饰3 */
4 String name;5 /**
6 * private修饰7 */
8 private intage;9 /**
10 * public修饰11 */
12 public char sex='M';13 /**
14 * protected修饰15 */
16 protected boolean isBeauty=true;
开始测试:如何获取
1 /**
2 * 反射机制获取类中的字段属性:Field字段3 *@throwsClassNotFoundException4 *@throwsSecurityException5 *@throwsNoSuchFieldException6 *@throwsIllegalAccessException7 *@throwsIllegalArgumentException8 */
9 @Test10 public void testFiled() throwsClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{11 //拿到反射Class对象
12 Class clazz = Class.forName("com.xfwl.reflection.Person");13 //获取Field的数组,私有字段也能获取
14 Field[] fields =clazz.getDeclaredFields();15 System.out.println("");16 for(Field field: fields) {17 System.out.println(field.getName());18 }19 System.out.println("");20 //获取指定名字的Field(如果是私有的,见下面的4)
21 Field field1 = clazz.getDeclaredField("name");22 System.out.println("获取指定Field名=: " + field1.getName()+",类型:"+field1.getType());23 Field field2 = clazz.getDeclaredField("age");24 System.out.println("获取指定Field名=: " + field2.getName()+",类型:"+field2.getType());25 Field field3 = clazz.getDeclaredField("sex");26 System.out.println("获取指定Field名=: " + field3.getName()+",类型:"+field3.getType());27 Field field4 = clazz.getDeclaredField("isBeauty");28 System.out.println("获取指定Field名=: " + field4.getName()+",类型:"+field4.getType());29
30 System.out.println("");31 Person person = new Person("小风微凉", 12,'M');32 //获取指定对象的Field的值
33 Object val =field1.get(person);34 System.out.println("获取指定对象字段'name'的Field的值=: " +val);35
36 System.out.println("");37 //设置指定对象的Field的值
38 field1.set(person, "反射学习A");39 System.out.println("设置指定对象字段'name'的Field的值=: " +person.name);40
41 System.out.println("");42 //若该字段是私有的,需要调用setAccessible(true)方法
43 field2 = clazz.getDeclaredField("age");44 field2.setAccessible(true);45 System.out.println("获取指定私有字段名=: " +field2.getName());46 }
测试结果:
name
age
sex
isBeauty获取指定Field名=: name,类型:classjava.lang.String
获取指定Field名=: age,类型:int获取指定Field名=: sex,类型:char获取指定Field名=: isBeauty,类型:boolean
有参构造!!!
获取指定对象字段'name'的Field的值=: 小风微凉设置指定对象字段'name'的Field的值=: 反射学习A获取指定私有字段名=: age
8、反射机制获取类中的构造器:构造器(Constructor)
Person的构造器
1 /**
2 * 无参构造3 */
4 publicPerson(){5 System.out.println("无参构造!!!");6 }7 /**
8 * 有参构造9 */
10 public Person(String name,int age,charsex){11 System.out.println("有参构造!!!");12 this.name=name;13 this.age=age;14 this.sex=sex;15 }
@Test测试
1 /**
2 * 构造器:开发用的比较少3 */
4 @Test5 public void testConstructor() throwsClassNotFoundException, NoSuchMethodException,6 IllegalAccessException, InvocationTargetException, InstantiationException {7 String className = "com.xfwl.reflection.Person";8 Class clazz = (Class) Class.forName(className);9
10 //1.获取Constructor对象
11 Constructor[] constructors =
12 (Constructor[]) Class.forName(className).getConstructors();13
14 System.out.println("");15 for (Constructorconstructor: constructors) {16 System.out.println(constructor);17 }18 System.out.println("");19 Constructor constructor = clazz.getConstructor(String.class, int.class,char.class);20 System.out.println("拿到指定的-->" +constructor);21
22 //2.调用构造器的newInstance()方法创建对象
23 Object obj= constructor.newInstance("changwen", 11,'M');24 }
运行结果:
publiccom.xfwl.reflection.Person()public com.xfwl.reflection.Person(java.lang.String,int,char)拿到指定的-->public com.xfwl.reflection.Person(java.lang.String,int,char)
有参构造!!!
9、反射机制获取类中的注解:注解(Annotation)
•从JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注释)
•Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理.通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息.
•Annotation可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的“name=value”对中.
•Annotation能被用来为程序元素(类,方法,成员变量等)设置元数据
基本的Annotation
•使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用.用于修饰它支持的程序元素
•三个基本的Annotation:
–@Override:限定重写父类方法,该注释只能用于方法
–@Deprecated:用于表示某个程序元素(类,方法等)已过时
–@SuppressWarnings:抑制编译器警告.
自定义Annotation
•定义新的Annotation类型使用@interface关键字
•Annotation的成员变量在Annotation定义中以无参数方法的形式来声明.其方法名和返回值定义了该成员的名字和类型.
•可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字
•没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation
1 packagecom.xfwl.reflection;2
3 import java.lang.annotation.ElementType;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.RetentionPolicy;
6 import java.lang.annotation.Target;7
8 public class Person extends Human implementsIHuman {9
10 @Retention(RetentionPolicy.RUNTIME) //运行时检验
11 @Target(value = {ElementType.METHOD}) //作用在方法上
12 public @interfaceAgeValidator {13
14 intmin();15 intmax();16 }17 //......其余部分省略
18 }
1 /**
2 * 自定义一个注解:检查年龄范围3 * @function4 *@author小风微凉5 * @time -6-3 下午3:56:036 */
7 @Retention(RetentionPolicy.RUNTIME) //运行时检验
8 @Target(value = {ElementType.METHOD}) //作用在方法上
9 public @interfaceAgeValidator {10 intmin();11 intmax();12 }
@Test测试
1 /**
2 * 通过反射才能获取注解3 */
4 @Test5 public void testAnnotation() throwsException {6 //这样的方式不能使用注解
7 /*Person person3 = new Person();8 person3.setAge(10);*/
9
10 //拿到反射Class对象
11 String className = "com.xfwl.reflection.Person";12 Class clazz =Class.forName(className);13 Object obj =clazz.newInstance();14 //拿到指定方法
15 Method method = clazz.getDeclaredMethod("setAge",int.class);16 int val =40;17
18 //获取注解
19 Annotation annotation = method.getAnnotation(AgeValidator.class);20 if (annotation != null){21 if (annotation instanceofAgeValidator){22 AgeValidator ageValidator =(AgeValidator) annotation;23
24 if (val< ageValidator.min() || val>ageValidator.max()){25 throw new RuntimeException("数值超出范围");26 }27 }28 }29 //执行方法
30 method.invoke(obj, val);31 System.out.println(obj);32 }
运行结果:
无参构造!!!
Person{name='null', age=40, sex='M'}
获取指定注解:
//获取注解
Annotation annotation = method.getAnnotation(AgeValidator.class);
获取所有注解:
1 //获取所有注解
2 Annotation[] arr=clazz.getDeclaredAnnotations();
提取Annotation信息
•JDK5.0在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注释的程序元素
•当一个Annotation类型被定义为运行时Annotation后,该注释才是运行时可见,当class文件被载入时保存在class文件中的Annotation才会被虚拟机读取
•程序可以调用AnnotationElement对象的如下方法来访问Annotation信息
–获取Annotation实例:
•getAnnotation(ClassannotationClass)
JDK的元Annotation
•JDK的元Annotation用于修饰其他Annotation定义
•@Retention:只能用于修饰一个Annotation定义,用于指定该Annotation可以保留多长时间,@Rentention包含一个RetentionPolicy类型的成员变量,使用@Rentention时必须为该value成员变量指定值:
–RetentionPolicy.CLASS:编译器将把注释记录在class文件中.当运行Java程序时,JVM不会保留注释.这是默认值
–RetentionPolicy.RUNTIME:编译器将把注释记录在class文件中.当运行Java程序时, JVM会保留注释.程序可以通过反射获取该注释
–RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释
•@Target:用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰哪些程序元素.@Target也包含一个名为value的成员变量.
•@Documented:用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档.
•@Inherited:被它修饰的Annotation将具有继承性.如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注释
后续补充整理:
(1)通过getDeclaredMethod拿到的方法,可以获取个中修饰符修饰的方法名称和对象,但是private的方法无法invoke执行
UserBean.java
1 packagecom.xfwl.reflect;2
3 public classUserBean {4
5 privateString uname;6 privateString upwd;7 publicUserBean(){8 this.setUname("xfwl");9 this.setUpwd("123456");10 }11 publicUserBean(String uname,String upwd){12 this.setUname(uname);13 this.setUpwd(uname);14 }15 public voidlogIn(UserBean user){16 System.out.println("用户登录:uname="+this.getUname()+",upwd="+this.getUpwd());17 }18 publicString getUname() {19 returnuname;20 }21 private voidlogOut(UserBean user){22 System.out.println("用户退出:uname="+this.getUname()+",upwd="+this.getUpwd());23 }24 public voidsetUname(String uname) {25 this.uname =uname;26 }27
28 publicString getUpwd() {29 returnupwd;30 }31
32 public voidsetUpwd(String upwd) {33 this.upwd =upwd;34 }35 }
View Code
测试类:ReflectAction.java
1 packagecom.xfwl.reflect;2
3 importjava.lang.reflect.Method;4
5
6 public classReflectAction {7 /**
8 *@paramargs9 */
10 public static voidmain(String[] args) {11 UserBean jack=null;12 UserBean tom=null;13 Class tomC=null;14 try{15 jack=(UserBean)Class.forName("com.xfwl.reflect.UserBean").newInstance();16 //jack.logIn();
17
18 tomC=Class.forName("com.xfwl.reflect.UserBean");19 tom=(UserBean) tomC.newInstance();20 Method[] methods =tomC.getDeclaredMethods();21 for(Method method : methods){22 if("logOut".equals(method.getName())){23 method=tomC.getDeclaredMethod(method.getName(),UserBean.class);24 System.out.println(method);25 method.invoke(tomC.newInstance(),tom);26 }27 }28 }catch(Exception e){29
30
31 }32
33 }34
35 }
View Code
运行结果:没有执行:logOut()
修改:
1 public voidlogOut(UserBean user){2 System.out.println("用户退出:uname="+this.getUname()+",upwd="+this.getUpwd());3 }
即可执行:logOut()