Java反射机制深度剖析

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习!java

      Java反射机制是Java语言中一种很重要的机制,可能在工做中用到的机会很少,可是在不少框架中都有用到这种机制。咱们知道Java是一门静态语言,在程序编译时变量的数据类型都已经肯定,那么在Java运行时环境中,对于任意一个类,咱们可否知道这个类有哪些属性和方法?对于任意一个对象,可否调用它的任意一个方法?答案是确定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java的反射机制(Reflection)。设计模式

 

      一、Java反射机制提供的功能数组

      主要提供了如下几个功能:框架

      1)在运行时判断任意一个对象所属的类;学习

      2)在运行时构造任意一个类的对象;spa

      3)在运行时判断任意一个类所具备的成员变量和方法;设计

      4)在运行时调用任意一个对象的方法。代理

      反射让Java具备了动态的特性,这种机制容许程序在运行时透过Reflection API获取任意一个已知名称的类的内部信息,包括成员变量(fields)、方法(methods)、实现的接口(interfaces)、Java语言修饰符(modifiers)以及它的父类(superclass)等等,并可在运行时改变成员变量的内容或调用方法。code

 

      二、Java Reflection API对象

      在JDK中,提供了如下类来实现Java反射机制,这些类都位于java.lang.reflect包下:

      Class类:表明一个类(注意:Class类位于java.lang包下);

      Field类:表明类的成员变量;

      Method类:表明类的方法;

      Constructor类:表明类的构造方法;

      Array类:提供了动态建立数组,以及访问数组的元素的静态方法。

      经过API提供的这些类里的方法,咱们能够动态获取想要的类的内部信息。

 

      三、获取类的Class对象

      Class类的实例表示正在运行的Java程序中的类和接口,每个类都有对应的Class对象,无论一个类生成了多少个对象,这些对象都对应内存里的同一个Class对象。Class类没有public的构造方法,Class对象是在加载类时由Java虚拟机自动构建的。

      有如下几种方式来获取一个类的Class对象:

      1)Class类提供的静态方法:forName(String className),参数className表示所需类的彻底限定名。     

 1 public class GetClassObject {
 2     
 3     public static void main(String[] args) throws Exception {
 4         
 5         Class<?> classType = Class.forName("java.lang.String");
 6         
 7         System.out.println(classType);//输出:class java.lang.String
 8     }
 9 
10 }

     

      2)运用.class语法     

 1 public class GetClassObject {
 2     
 3     public static void main(String[] args) throws Exception {
 4         
 5         Class<?> classType = String.class;
 6         
 7         System.out.println(classType);//输出:class java.lang.String
 8     }
 9 
10 }

     

      3)Object类提供的方法:getClass()     

 1 public class GetClassObject {
 2     
 3     public static void main(String[] args) throws Exception {
 4         
 5         Map map = new HashMap();
 6         Class<?> classType = map.getClass();
 7         
 8         System.out.println(classType);//输出:class java.util.HashMap
 9     }
10 
11 }

 

      四、获取类的Field(成员变量)对象

      类的每个成员变量都对应一个Field对象,Class类提供了如下方法来获取类的成员变量对应的Field对象:

      1)Field getDeclaredField(String name):根据传入的变量名称返回此Class对象所表示的类或接口中声明的变量对应的Field对象。

      2)Field[] getDeclaredFields():返回一个Field类型的数组,包含此Class对象所表示的类或接口中声明的全部变量的Field对象。

      3)Field getField(String name):根据传入的变量名返回一个Field对象,注意与getDeclaredField(String name)不一样的是,此方法返回的是public变量对应的Field对象。

      4)Field[] getFields():返回一个Field类型的数组,注意与Field[] getDeclaredFields()方法不一样的是,此方法返回的是全部public变量对应的Field对象。

      代码示例:

 1 public class GetFieldObject {
 2     
 3     public static void main(String[] args) throws Exception {
 4         
 5         //首先,得到String类的Class对象
 6         Class<?> classType = Class.forName("java.lang.String");
 7         
 8         //得到String类中声明的全部成员变量的Field对象的数组
 9         Field[] fields = classType.getDeclaredFields();
10         for(Field field : fields){
11             System.out.println(field);
12         }    
13         
14         System.out.println("---------------------------------------------------------------------");
15         
16         //得到String类中声明的public成员变量的Field对象的数组
17         Field[] publicFields = classType.getFields();
18         for(Field field : publicFields){
19             System.out.println(field);
20         }
21         
22     }
23 
24 }

      输出结果:

      

      从结果输出能够看出getDeclaredFields()与getFields()的区别:getDeclaredFields()返回的是全部属性的Field对象;而getFields()返回的是声明为public的属性的Field对象。

 

      五、获取类的Method对象

      类中的每个方法都对应一个Method对象,Class类提供了如下方法来获取类中的方法对应的Method对象:

      1)Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回一个Method对象,参数name表示方法名,可变参数parameterTypes是一个Class对象的数组,表明方法的参数的Class类型;

      2)Method[] getDeclaredMethods():返回Method对象的一个数组,这些对象反映此Class对象所表示的类或接口声明的全部方法,包括公共、保护、默认访问和私有方法,但不包括继承的方法;

      3)Method getMethod(String name, Class<?>... parameterTypes):返回一个Method对象,注意和此Method对象对应的方法是公共(public)方法;

      4)Method[] getMethods():返回一个Method数组,这些对象反映此Class对象所表示的类或接口中声明的公共(public)方法(也包括父类或父接口中声明的public方法)。
      代码示例:     

 1 public class GetMethodObject {
 2     
 3     public static void main(String[] args) throws Exception {
 4         
 5         //首先,得到类的Class对象
 6         Class<?> classType = Class.forName("java.lang.reflect.Proxy");
 7         
 8         //得到类中声明的全部方法的Method对象的数组,不包括继承的父类的方法
 9         Method[] methods = classType.getDeclaredMethods();
10         for(Method method : methods){
11             System.out.println(method);
12         }    
13         
14         System.out.println("----------------------------------------------------------------------");
15         
16         //得到类中的public方法的Method对象的数组,也包括继承的父类的public方法
17         Method[] publicMethods = classType.getMethods();
18         for(Method method : publicMethods){
19             System.out.println(method);
20         }
21         
22     }
23 
24 }

      输出结果:

      

 

      六、用反射机制调用对象的方法

      Java反射机制能够在运行时动态调用类中的方法,Java Reflection API提供了咱们所需的方法来完成动态调用。要想调用类中的方法首先要建立一个对象,咱们经过类的Class对象来建立它所表明的类的实例,经过Class对象咱们还能得到类中声明的方法的Method对象,Method类提供了Invoke方法来调用此Method对象所表示的方法。反射机制调用方法代码示例以下:   

 1 public class InvokeTester {
 2     
 3     public static int add(int a, int b){
 4         return a + b;
 5     }
 6     
 7     public static String echo(String str){
 8         return "hello "+str;
 9     }
10     
11     
12     public static void main(String[] args) throws Exception {
13 //        InvokeTester invoke = new InvokeTester();
14 //        System.out.println(invoke.add(1, 2));
15 //        System.out.println(invoke.echo("tom"));
16         
17         
18         //用反射机制调用,首先得到类的Class对象
19         Class<?> classType = InvokeTester.class;
20         
21         //经过Class对象得到一个InvokeTester类的实例
22         Object invoke = classType.newInstance();
23         
24         //得到add(int a, int b)方法的Method对象,getMethod方法的参数为方法名和方法参数类型的Class对象的数组
25         Method addMethod = classType.getMethod("add", int.class, int.class);
26         
27         //经过Method类的invoke方法,调用invoke对象的add方法
28         Object result = addMethod.invoke(invoke, 1, 2);
29         
30         System.out.println(result);
31         
32         Method echoMethod = classType.getMethod("echo", String.class);
33         
34         Object result2 = echoMethod.invoke(invoke, "Tom");
35         
36         System.out.println(result2);
37         
38     }
39 }

 

      七、用反射机制调用类的私有方法

      咱们知道正常状况下一个类的私有方法只容许这个类自己来调用,但使用反射机制能打破这种访问限制,让其余的类也能调用这个类的私有的方法。这种场景在实际开发中不多用到,Java也不提倡这种用法。代码示例以下:  

public class Private {
    
    //定义一个私有方法
    private String sayHello(String name){
        return "hello, "+name;
    }

}


public class PrivateTest {
    
    public static void main(String[] args) throws Exception {
        //调用Private类的私有方法
        Private p = new Private();
        
        Class<?> classType = p.getClass();
        
        Method method = classType.getDeclaredMethod("sayHello", String.class);
        
        method.setAccessible(true);//取消Java访问检查,若是不设置此项则会报错
        
        String str = (String)method.invoke(p, "Tracy");
        
        System.out.println(str);//输出:hello, Tracy
    }
    
}

      Method、Field、Constructor类有一个共同的父类AccessibleObject类,它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。在上面的代码中,咱们在反射对象Method中设置accessible标志,它容许程序以某种一般禁止的方式来操做对象。

 

      八、用反射机制操做类的私有变量

      与前面调用类的私有方法相似,经过反射咱们还能操做类的私有变量,代码示例以下:   

 1 public class Private2 {
 2     //定义私有变量
 3     private String name = "zhangsan";
 4     
 5     public String getName(){
 6         return name;
 7     }
 8 }
 9 
10 
11 public class PrivateTest2 {
12     
13     public static void main(String[] args) throws Exception {
14         //改变Private2类的私有变量的值
15         Private2 p = new Private2();
16         
17         Class<?> classType = p.getClass();
18         
19         Field field = classType.getDeclaredField("name");
20         
21         field.setAccessible(true);//取消默认java访问控制检查,Field类的父类AccessibleObject类提供的方法
22         
23         field.set(p, "lisi");//Field类的set(Object obj, Object value)方法将指定对象上此Field对象表示的字段设置为指定的新值
24         
25         System.out.println(p.getName());//输出:lisi
26         
27     }
28     
29 }

      以上这些内容,我介绍了Java反射机制的中涉及的主要的几个类以及这些类的基本用法,这些类中还有不少的方法,你们能够经过查看API进行了解,用法都很简单。Java反射机制在不少框架的底层实现中有用到,还有一种很重要的设计模式也用到了反射,那就是代理模式中的动态代理,了解了动态代理模式的思想对咱们研究框架有很大帮助,我会在后面的博客中介绍这些内容,欢迎你们共同探讨。

相关文章
相关标签/搜索