返回顶部

Java反射学习:深入学习Java反射机制

一、Java反射的理解(反射是研究框架的基础之一)

 Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

二、逐步分析

参考:https://blog.csdn.net/u012585964/article/details/52011138

 

1、关于Class

  1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
    2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
    3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。
    4、Class 对象只能由系统建立对象
    5、一个类在 JVM 中只会有一个Class实例

下面创建一个例子:后面的分析均以此案例为基准。

创建一个空接口:(后面会不断补充) 

1 package com.xfwl.reflection;
2 
3 public interface IHuman {
4 
5 }

 创建一个空基类:(后面会不断补充) 

1 package com.xfwl.reflection;
2 
3 public class Human {
4     
5 }

 创建一个子类:(后面会不断补充) 

 1 package com.xfwl.reflection;
 2 
 3 public class Person extends Human implements IHuman {
 4     /**
 5      * 默认default修饰
 6      */
 7     String name;
 8     /**
 9      * private修饰
10      */
11     private int age;
12     /**
13      * public修饰
14      */
15     public char sex='M';
16     /**
17      * 无参构造
18      */
19     public Person(){
20         System.out.println("无参构造!!!");
21     }
22     /**
23      * 有参构造
24      */
25     public Person(String name,int age,char sex){
26         System.out.println("有参构造!!!");
27         this.name=name;
28         this.age=age;
29         this.sex=sex;
30     }
31     public String getName() {
32         return name;
33     }
34     public void setName(String name) {
35         this.name = name;
36     }
37     public int getAge() {
38         return age;
39     }
40     public void setAge(int age) {
41         this.age = age;
42     }
43     public char getSex() {
44         return sex;
45     }
46     public void setSex(char sex) {
47         this.sex = sex;
48     }
49     public String toString() {  
50         return "Person{" +  
51                 "name='" + name + '\'' +  
52                 ", age=" + age  +  
53                 ", sex='" + sex + '\'' +  
54                 '}';  
55     }  
56 }

 2、反射获取类对象的三种方式(通过一个Junit测试来说明) 

 1 package com.xfwl.reflection;
 2 
 3 import org.junit.Test;
 4 /**
 5  * 测试类
 6  * @function  
 7  * @author 小风微凉
 8  * @time  2018-6-3 下午12:28:38
 9  */
10 public class TestAction {
11     /** 
12      * 反射机制获取类有三种方法 
13      */ 
14     @Test
15     public void testGetClass() throws ClassNotFoundException {  
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 = new Person();  
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 }

 运行结果: 

通过类名: class com.xfwl.reflection.Person
无参构造!!!
通过getClass(): class com.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      * @throws ClassNotFoundException
 4      * @throws IllegalAccessException
 5      * @throws InstantiationException
 6      */
 7     @Test
 8     public void testNewInstance()  
 9             throws ClassNotFoundException, 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类加载器


类加载器详解:
http://blog.csdn.net/ochangwen/article/details/51473120
 
 1     /** 
 2      * ClassLoader类装载器 
 3      */  
 4     @Test  
 5     public void testClassLoader1() throws ClassNotFoundException, 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      * @throws ClassNotFoundException
 4      * @throws IOException
 5      */
 6      @Test  
 7     public void testGetResourceAsStream() throws ClassNotFoundException, IOException {  
 8         //调用getResourceAsStream 获取类路径下的文件对应的输入流 
 9          /**
10           * 特别说明:
11           * getResourceAsStream("path"),path的路径和new Person()的位置有关
12           */
13          
14         InputStream in = new Person().getClass().getClassLoader()  
15                 .getResourceAsStream("com/xfwl/reflection/test.properties");  
16         System.out.println("in: " +in);  
17   
18         Properties properties = new Properties(); 
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=23
sex=M
desc=\u53CD\u5C04\u673A\u5236\u5B66\u4E60

 运行结果:(直接解析会出现乱码问题,这个可以通过new String(乱码格式处理参数)来处理)

无参构造!!!
in: java.io.BufferedInputStream@215be6bb
文件内容:{age=23, name=小风微凉??, sex=M, desc=反射机制学习}
name: 小风微凉??
age: 23
sex: M
desc: 反射机制学习

 6、反射机制获取类中的方法:Method: 对应类中的方法 

现在给Person类添加一个private 方法、一个public 方法、一个defaut 方法、一个protected方法 

 1 /**
 2      * Java权限有四个,分别为public,protected,默认,private,其开放程度依次降低
 3      *    public可供所有类访问
 4      *    protected继承可见
 5      *    private只能类本身内部的方法可以访问
 6      */    
 7     public void method_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 void method_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     void method_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 void method_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      * @throws ClassNotFoundException
 4      * @throws NoSuchMethodException
 5      * @throws IllegalAccessException
 6      * @throws InstantiationException
 7      * @throws InvocationTargetException
 8      */
 9     @Test  
10     public void testMethod() throws ClassNotFoundException, 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 void com.xfwl.reflection.Person.method_private()
private 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
public 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
无参构造!!!
method_public_2
Person{name='小风微凉', age=23, sex='M'}

 继续分析一下:

JDK中的获取方法

获取方法:默认只能获取public修饰的方法 

1 @CallerSensitive
2     public Method[] getMethods() throws SecurityException {
3         checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
4         return copyMethods(privateGetPublicMethods());
5     }

 获取方法:所有修饰权限的方法都可以获得 

@CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }

 获取方法:获取指定的方法(所有修饰权限) 

 1     /**
 2      * @jls 8.2 Class Members
 3      * @jls 8.4 Method Declarations
 4      * @since JDK1.1
 5      */
 6     @CallerSensitive
 7     public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 8         throws NoSuchMethodException, 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         return method;
15     }

   分析一下上面这个方法:

    String name:方法的名称

       Class<?>... parameterTypes:一个或多个方法参数的类型,注意要一一对应,否则会报错的哦

执行方法:invoke(方法对象,方法实际参数) 

 1 @CallerSensitive
 2     public Object invoke(Object obj, Object... args)
 3         throws IllegalAccessException, IllegalArgumentException,
 4            InvocationTargetException
 5     {
 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         return ma.invoke(obj, args);
17     }

 7、反射机制获取类中的方法:Method: 对应基类或接口中的方法

上面分析了,如何通过反射拿到当前本类里面的各个修饰权限的方法,下面来继续分析一下,如何读取父类或实现的接口中的方法:

现在,给父类添加一些方法,在接口中定义一些方法:

接口中的方法声明: 

1 package com.xfwl.reflection;
2 
3 public interface IHuman {
4 
5     void eat();
6     void eat(String info);
7 }

 Person.java实现接口方法

1     public void eat() {
2         System.out.println("实现接口的方法:eat()无参:");
3     }
4     public void eat(String info) {
5         System.out.println("实现接口的方法:eat()有参:"+info);        
6     }

 父类中的方法定义: 

 1 package com.xfwl.reflection;
 2 
 3 public class Human {
 4     public void play_public(){
 5         System.out.println("public无参:play_public");
 6     }
 7     public void play_public_2(String info){
 8         System.out.println("public有参:play_public2:"+info);
 9     }
10     protected void play_protected(){
11         System.out.println("protected无参:play_protected");
12     }
13     protected void play_protected_2(String info){
14         System.out.println("protected有参:play_protected_2:"+info);
15     }
16     void play_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 void play_private(){
23         System.out.println("private无参:play_private");
24     }
25     private void play_private_2(String info){
26         System.out.println("private有参:play_private_2:"+info);
27     }
28 }

 开始测试:

1、拿到当前Person类反射对象,能否得到接口中的方法 

 1 /**
 2      * 反射机制获取类中的方法:Method: 对应基类或接口中的方法
 3      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchMethodException 
 6      * @throws InstantiationException 
 7      * @throws InvocationTargetException 
 8      * @throws IllegalArgumentException 
 9      * @throws IllegalAccessException 
10      */
11     @Test
12     public void testInterfaceOrSupperClass() throws ClassNotFoundException, 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      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchMethodException 
 6      * @throws InstantiationException 
 7      * @throws InvocationTargetException 
 8      * @throws IllegalArgumentException 
 9      * @throws IllegalAccessException 
10      */
11     @Test
12     public void testInterfaceOrSupperClass() throws ClassNotFoundException, 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
notifyAll
<----------------父类中所有的方法:仅仅父类中的方法-------------------->
play_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 int age;
 9     /**
10      * public修饰
11      */
12     public char sex='M';
13     /**
14      * protected修饰
15      */
16     protected boolean isBeauty=true;

 开始测试:如何获取 

 1  /**
 2      * 反射机制获取类中的字段属性:Field字段
 3      * @throws ClassNotFoundException 
 4      * @throws SecurityException 
 5      * @throws NoSuchFieldException 
 6      * @throws IllegalAccessException 
 7      * @throws IllegalArgumentException 
 8      */
 9     @Test
10     public void testFiled() throws ClassNotFoundException, 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("<------------获取指定名字的Field以及类型--------------------------->");
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("<----------获取指定对象的Field的值 ----------------------------->");
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("<----------设置指定对象的Field的值----------------------------->");
37         //设置指定对象的Field的值  
38         field1.set(person, "反射学习A");  
39         System.out.println("设置指定对象字段'name'的Field的值=: " + person.name);  
40          
41         System.out.println("<----------若该字段是私有的,需要调用setAccessible(true)方法----------------------------->");
42         //若该字段是私有的,需要调用setAccessible(true)方法  
43         field2 = clazz.getDeclaredField("age");  
44         field2.setAccessible(true);  
45         System.out.println("获取指定私有字段名=: " + field2.getName());  
46     }

 测试结果: 

<----遍历拿到字段:开始--------------------->
name
age
sex
isBeauty
<------------获取指定名字的Field以及类型--------------------------->
获取指定Field名=: name,类型:class java.lang.String
获取指定Field名=: age,类型:int
获取指定Field名=: sex,类型:char
获取指定Field名=: isBeauty,类型:boolean
<----------获取指定对象的Field的值 ----------------------------->
有参构造!!!
获取指定对象字段'name'的Field的值=: 小风微凉
<----------设置指定对象的Field的值----------------------------->
设置指定对象字段'name'的Field的值=: 反射学习A
<----------若该字段是私有的,需要调用setAccessible(true)方法----------------------------->
获取指定私有字段名=: age

 8、反射机制获取类中的构造器:构造器(Constructor)

Person的构造器

 1 /**
 2      * 无参构造
 3      */
 4     public Person(){
 5         System.out.println("无参构造!!!");
 6     }
 7     /**
 8      * 有参构造
 9      */
10     public Person(String name,int age,char sex){
11         System.out.println("有参构造!!!");
12         this.name=name;
13         this.age=age;
14         this.sex=sex;
15     }

 @Test测试 

 1 /** 
 2      * 构造器:开发用的比较少 
 3      */  
 4     @Test  
 5     public void testConstructor() throws ClassNotFoundException, NoSuchMethodException,  
 6             IllegalAccessException, InvocationTargetException, InstantiationException {  
 7         String className = "com.xfwl.reflection.Person";  
 8         Class<Person> clazz = (Class<Person>) Class.forName(className);  
 9       
10         //1.获取Constructor对象  
11         Constructor<Person>[] constructors =  
12                 (Constructor<Person>[]) Class.forName(className).getConstructors();  
13 
14         System.out.println("<-------------打印所有的构造器---------------------->");
15         for (Constructor<Person> constructor: constructors) {  
16             System.out.println(constructor);  
17         }  
18         System.out.println("<------------------------------------------------>");
19         Constructor<Person> 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     }

  运行结果: 

<-------------打印所有的构造器---------------------->
public com.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 package com.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 implements IHuman {
 9 
10     @Retention(RetentionPolicy.RUNTIME) //运行时检验  
11     @Target(value = {ElementType.METHOD})  //作用在方法上  
12     public @interface AgeValidator {  
13       
14         int min();  
15         int max();  
16     }
17     //......其余部分省略  
18 }

  

 1 /**
 2      * 自定义一个注解:检查年龄范围
 3      * @function  
 4      * @author 小风微凉
 5      * @time  2018-6-3 下午3:56:03
 6      */
 7     @Retention(RetentionPolicy.RUNTIME) //运行时检验  
 8     @Target(value = {ElementType.METHOD})  //作用在方法上  
 9     public @interface AgeValidator { 
10         int min();  
11         int max();  
12     } 

 @Test测试 

 1  /** 
 2      * 通过反射才能获取注解 
 3      */  
 4     @Test  
 5     public void testAnnotation() throws Exception {  
 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 instanceof AgeValidator){  
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(Class<T> annotationClass)

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 package com.xfwl.reflect;
 2 
 3 public class UserBean {
 4 
 5     private String uname;
 6     private String upwd;
 7     public UserBean(){
 8         this.setUname("xfwl");
 9         this.setUpwd("123456");
10     }
11     public UserBean(String uname,String upwd){
12         this.setUname(uname);
13         this.setUpwd(uname);
14     }
15     public void logIn(UserBean user){
16         System.out.println("用户登录:uname="+this.getUname()+",upwd="+this.getUpwd());
17     }
18     public String getUname() {
19         return uname;
20     }
21     private void logOut(UserBean user){
22         System.out.println("用户退出:uname="+this.getUname()+",upwd="+this.getUpwd());
23     }
24     public void setUname(String uname) {
25         this.uname = uname;
26     }
27 
28     public String getUpwd() {
29         return upwd;
30     }
31 
32     public void setUpwd(String upwd) {
33         this.upwd = upwd;
34     }    
35 }
View Code

 测试类:ReflectAction.java

 1 package com.xfwl.reflect;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 
 6 public class ReflectAction {
 7     /**
 8      * @param args
 9      */
10     public static void main(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 void logOut(UserBean user){
2         System.out.println("用户退出:uname="+this.getUname()+",upwd="+this.getUpwd());
3     }

  即可执行:logOut()

posted @ 2018-06-03 16:10  小风微灵-彦  阅读(2196)  评论(0编辑  收藏  举报
加载中……