java反射技术

在很多的Web框架中不论是spring,struts等都使用了反射技术,那发射是什么呢?反射本身并不是一个新概念,尽管计算机科学赋予了反射概念新的含义。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java 语言的反射机制。Java 反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。 

因此反射其实就是加载并解析出类的各种属性和行为来,比如在各种框架中,我们配置好各种配置文件后,交给框架加载并执行我们的类。

java的反射包java.lang.reflect是jdk提供的反射机制API,常用到的类并不多,主要包括以下类:

1).Constructor 类:用来描述一个类的构造方法  

2).Field 类:用来描述一个类的成员变量

3).Method 类:用来描述一个类的方法.

4).Modifer 类:用来描述类内各元素的修饰符

5).Array:用来对数组进行操作.

我们可以通过反射API 来在程序运行过程中取得任何一个已知名称的类的内部信息,包括类的修饰符(public,static 等),基类(超类,父类),实现的接口,字段和方法等信息,并可以可以根据字节码信息来创建该类的实例对象,改变对象的字段内容和调用对象方法。

首先不得不讲一讲Class类 ,该类表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象。Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

Class类的forName()方法,可以显示加载类。使用如下:

 Class clazz = Class.forName("com.demo.reflectionDemo.Person");

     Class clazz1 = new Person().getClass();

         Class clazz2 = Person.class

获得了类的实例,我们就可以通过反射API得到类的各种属性方法了。就像你已经得到了一个类之后,要使用该类的什么属性,你可以通过反射API来解剖出类的什么属性。

1.获得构造函数

使用Constructor类来解剖类的构造函数,使用Class的如下方法获得构造函数:

Constructor[] getDeclaredConstructors():返回已加载类声明的所有的构造方法的Constructor 对象数组。

Constructor getDeclaredConstructor(Class[] paramTypes):返回已加载类声明的指定构造方法的Constructor 对象,paramTypes 指定了参数类型。

Constructor[] getConstructors():返回已加载类声明的所有的public 类型的构造方法的Constructor 对象数组。

Constructor getConstructor(Class[] paramTypes): 返回已加载类声明的指定的public 类型的构造方法的Constructor 对象,paramTypes 指定了参数类型。

 构造实例如下:

Constructor c = clazz.getConstructor(null);  // public person()

Constructor c = clazz1.getConstructor(String.class); // public person(String name);

Constructor c = clazz2.getConstructor(String.class,String.class);  //public person(String name,String password);

Constructor c = clazz3.getDeclaredConstructor(List.class);  //private person(List name); 

然后便可以通过构造函数直接构造出一个类的实例来, c.newInstance(), 根据参数的多少有无来的情况来使用参数。如果是私有的属性,可通过方法 c.setAccessible(true) 来打开属性。

 2.获得类的方法

反射API提供了Method类,还有如下方法:

Method[] getDeclaredMethods():返回已加载类声明的所有方法的Method 对 象数组,不包括从父类继承的方法 

Method getDeclaredMethod(String name,Class[] paramTypes): 返回已加载 类声明的所有方法的Method 对象,不包括从父类继承的方法,参数name 指定方 法的名称,参数paramTypes 指定方法的参数类型

Method[] getMethods():返回已加载类声明的所有方法的Method 对象数组,包 括从父类继承的方法

Method getMethod(String name,Class[] paramTypes): 返回已加载类声明的 所有方法的Method 对象,包括从父类继承的方法,参数name 指定方法的名称,参 数paramTypes 指定方法的参数类型.

 使用如下:

Method method = clasz.getMethod("print", null);    //public  void print();

Method method = clasz.getMethod("validation",String.class,int.class); //public void validation(String name,int id);

Method method = clasz.getMethod("getOrder",String.class,int[].class);  // public void getOrder(String name,int[] number);

Method method = clasz.getDeclaredMethod("copyTo",InputStream.class);  //private void copyTo(InputStream in);

  通过使用invoke方法来调用该方法,method.invoke(), method.invoke(obj,"jack",1)等等,同样如果是不可访问的方法,则可以通过method.setAccessible(true)打开访问权限。如果是静态方法,则其他相同,在调用的时候将object置为null,因为静态方法调用不需要对象。

如果是反射main函数的话,因为main函数的参数是public static void main(String[]args),在invoke的时候需要 method.invoke(null,(Object)new String{"param1","param2","param3"}才是正确的。

3.获取方法的属性域Field

Field[] getDeclaredFields():返回已加载类声明的所有成员变量的Field 对象数 组,不包括从父类继承的成员变量 

Field getDeclaredField(String name): 返回已加载类声明的所有成员变量的 Field 对象,不包括从父类继承的成员变量,参数name 指定成员变量的名称

Field[] getFields():返回已加载类声明的所有public 型的成员变量的Field 对象 数组,包括从父类继承的成员变量

Field getField(String name):返回已加载类声明的所有成员变量的Field 对象, 包括从父类继承的成员变量,参数name 指定成员变量的名称

使用实例如下:

Field f = claz.getField("name");  

    Object value = f.get(p);
    Class type = f.getType();
    if(type.equals(String.class)){
     String svalue = (String)value; System.out.println(svalue);
    }
    f.set(p, "xxxxxxxxx");

 此外,还可以获取类的其他信息:

int getModifiers():返回已加载类的修饰符的整形标识值.
Package getPackage():返回已加载类的包名
Class getSuperclass(): 返回已加载类的父类的Class 实例.
Class [] getInterfaces():返回已加载类实现的接口的Class 对象数组.
boolean isInterface():返回已加载类是否是接口.
反射的功能很强大,但是使用不当可能会缺点大于优点,反射使代码逻辑混乱,会带来维护的问题.

 

 开发框架是,经常需要使用java对象的属性来封装程序的数据,每次都使用发射技术完成此类的操作过于麻烦,所有sun公司开发了一套API,专门用于操作java对象的属性。内省访问javabean属性的两种方式:

1)通过PropertyDescriptor 类操作Bean的属性。

2)通过Introspector类获得Bean对象的BeanInfo,然后BeanInfo来获取属性的描述器,通过这个属性的描述器就可以获取某个属性对应的getter/setter方法,然后通过反射机制来调用这些方法。

 使用Introspector类分析 bean 的类和超类,寻找显式或隐式信息,使用这些信息构建一个全面描述目标 bean 的 BeanInfo 对象。使用实例如下:

BeanInfo info = Introspector.getBeanInfo(Person.class, Object.class); //获取person自己的属性 

        PropertyDescriptor[] pds = info.getPropertyDescriptors();
        for (PropertyDescriptor pd : pds) {
            System.out.println(pd.getName());

        } 

 下列是获取指定属性:

PropertyDescriptor pds = new PropertyDescriptor("age", Person.class);

        Method method = pds.getWriteMethod();
        method.invoke(p, 12);
        method = pds.getReadMethod();

        System.out.println(method.invoke(p, null)); 

  获取当前操作的属性类型:

PropertyDescriptor pds = new PropertyDescriptor("password", Person.class);  

System.out.println(pds.getPropertyType());

如果通过类型操作Bean的属性有些麻烦,apache有一个BeanUtils包,可以用来专门操作Bean的属性,关于BeanUtils的使用参看网上资料。

 

原文地址:https://www.cnblogs.com/kingcucumber/p/2831718.html