反射机制基础解析

来源:互联网 发布:ipad下载软件付费 编辑:程序博客网 时间:2024/05/22 23:27

个人博客:www.letus179.com

概念

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.

通俗来讲:反射就是把Class对象的各种成分映射成对应的Java类


作用

java反射机制其实就是将.class转化为.java,也即反编译。具体主要提供了以下功能:

  • 在运行时判断任意一个对象所属的
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量方法
  • 在运行时调用任意一个对象的方法。

后面会围绕这几点具体展开。


重要API

java.lang.reflect包下提供类和接口,以获得关于类和对象的反射信息。这里简单罗列了与反射相关的几个重要的API,真正想学习反射机制,除了在项目中历练,API也是非常重要的手段。英语不好理解的话,就搜搜汉化后的API文档。

变量相关 含义 getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段方法相关 含义 getMethod(String name, Class… parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 getDeclaredMethod(String name, Class… parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 构造方法相关 含义 getConstructor(Class… parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法 getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法 getDeclaredConstructor(Class… parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法 父类、父接口相关 含义 getInterfaces() 确定此对象所表示的类或接口实现的接口。 getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。 其他重要相关 含义 getModifiers() 以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。 getName() 以 String 形式返回此 Method 对象表示的方法名称。 getReturnType() 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。 newInstance() 创建此 Class 对象所表示的类的一个新实例。 isInstance(Object obj) 判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。

具体功能实现

下面通过例子分别讲述获取Class对象的3种方式如何创建实例如何获取构造器如何获取方法如何获取属性以及通过反射调用方法

获取Class对象的3种方式

例如新建一个类MyReflectTest,代码如下:

public class MyReflectTest {}

方式1:使用Class类的中静态forName()方法获得与字符串对应的Class对象

    public static void main(String[] args) {        try {          Class<?> clz = Class.forName("test.MyReflectTest");        }        catch (ClassNotFoundException e) {          e.printStackTrace();        }    }

在数据库有关开发中,我们经常会用到这个方法,例如:

 Class.forName("com.mysql.jdbc.Driver")

方式2: 利用对象的class属性

  public static void main(String[] args) {    Class clz = MyReflectTest.class;  }

注意,在基本类型和包装类型中有,例如:

Integer.TYPE

等价于

int.class

intClass对象,不等价于

Integer.class

这是IntegerClass对象表示,看看源码就知道了。

方式3:调用对象的getClass()方法

  public static void main(String[] args) {    MyReflectTest myReflectTest = new MyReflectTest();    Class clz = myReflectTest.getClass();  }

创建实例

获取到class对象后,调用newInstance()方法来创建Class对象对应的类实例,
测试代码:

  public static void main(String[] args) throws InstantiationException, IllegalAccessException {    Class clz = MyReflectTest.class;    MyReflectTest newInstance = (MyReflectTest) clz.newInstance();  }

获取构造器

获取类的所有构造器,测试代码:

  public static void main(String[] args) {    // 获取类对象    Class clz = MyReflectTest.class;    // 获取public构造器数组    Constructor[] cons = clz.getConstructors();    // 获取public,默认,protected,private构造器数组    Constructor[] declaredCons = clz.getDeclaredConstructors();  }

获取类中指定的某个构造器,测试代码:

  public MyReflectTest(String name) {    super();    this.name = name;  }  MyReflectTest(String name, int age) {    super();    this.name = name;    this.age = age;  }  public static void main(String[] args) throws NoSuchMethodException, SecurityException {    // 获取类对象    Class clz = MyReflectTest.class;    // 获取public构造器,构造器参数类型为String    Constructor con = clz.getConstructor(String.class);    // 获取public,默认,protected,private构造器,构造器参数类型为String,int    Constructor declaredCon = clz.getDeclaredConstructor(String.class, int.class);  }

获取方法

新增两个方法:add()get()

  private void add(String name) {    System.out.println(name);  }  public int get() {    System.out.println(age);    return age;  }

getDeclaredMethods()方法相关测试代码:

  public static void main(String[] args) throws NoSuchMethodException, SecurityException {    // 获取类对象    Class clz = MyReflectTest.class;    Method[] declaredMethods = clz.getDeclaredMethods();    for (Method method : declaredMethods) {      System.out.println("===============================");      // 访问修饰符      System.out.println(Modifier.toString(method.getModifiers()));      // 返回类型      System.out.println(method.getReturnType());      // 方法名称      System.out.println(method.getName());      Class<?>[] parameterTypes = method.getParameterTypes();      for (Class<?> param : parameterTypes) {        // 方法参数类型        System.out.println(param.getName());      }    }  }

执行结果:

===============================public staticvoidmain[Ljava.lang.String; //"["表示数组对象===============================privatevoidaddjava.lang.String===============================publicintget

能获取私有的方法add()
看看另一个反射方法:getMethods(),代码和上面一个,基本没变化。
测试代码:

  public static void main(String[] args) throws NoSuchMethodException, SecurityException {    // 获取类对象    Class clz = MyReflectTest.class;    Method[] methods = clz.getMethods();    for (Method method : methods) {      System.out.println("+++++++++++++++++++++++++++++++++");      System.out.println(Modifier.toString(method.getModifiers()));      System.out.println(method.getReturnType());      System.out.println(method.getName());      Class<?>[] parameterTypes = method.getParameterTypes();      for (Class<?> param : parameterTypes) {        System.out.println(param.getName());      }    }  }

执行结果:

+++++++++++++++++++++++++++++++++public staticvoidmain[Ljava.lang.String;+++++++++++++++++++++++++++++++++publicintget+++++++++++++++++++++++++++++++++public finalvoidwaitlongint+++++++++++++++++++++++++++++++++public final nativevoidwaitlong+++++++++++++++++++++++++++++++++public finalvoidwait+++++++++++++++++++++++++++++++++publicbooleanequalsjava.lang.Object+++++++++++++++++++++++++++++++++publicclass java.lang.StringtoString+++++++++++++++++++++++++++++++++public nativeinthashCode+++++++++++++++++++++++++++++++++public final nativeclass java.lang.ClassgetClass+++++++++++++++++++++++++++++++++public final nativevoidnotify+++++++++++++++++++++++++++++++++public final nativevoidnotifyAll

对比上面的getDeclaredMethods(),少了一个私有的方法add(),但是多了好多不在本类的方法。由于每个类的超类都是Object类,很明显,这些方法都是来自超类,看看源码也能发现这个。也就是说:

  • getDeclaredMethods()获取的是本类的方法,public、默认、protected、private;
  • getMethods()获取的是本类和父类的所有的public的方法。

另外有获取指定某一个方法的反射方法。
测试代码:

  public String save(String name, int age) {    return "name: " + name + ", age: " + age;  }  public static void main(String[] args) throws NoSuchMethodException, SecurityException {    // 获取类对象    Class clz = MyReflectTest.class;    // 第一个参数是“方法名”,后面的是方法的可变参数列表    Method method = clz.getMethod("save", String.class, int.class);    System.out.println(Modifier.toString(method.getModifiers()));  }

执行结果:

public

其他的类似。

获取属性

创建两个成员变量nameage,如下:

  public String name;  private int age;

测试代码:

  public static void main(String[] args) throws NoSuchMethodException, SecurityException {    // 获取类对象    Class clz = MyReflectTest.class;    Field[] fields = clz.getFields();    for (Field field : fields) {      System.out.println("+++++++++++++++++++++++++");      System.out.println(Modifier.toString(field.getModifiers()));      System.out.println(field.getType());      System.out.println(field.getName());    }    Field[] declaredFields = clz.getDeclaredFields();    for (Field field : declaredFields) {      System.out.println("=========================");      System.out.println(Modifier.toString(field.getModifiers()));      System.out.println(field.getType());      System.out.println(field.getName());    }  }

执行结果:

+++++++++++++++++++++++++publicclass java.lang.Stringname=========================publicclass java.lang.Stringname=========================privateintage

另外getField(String)getDeclaredField(String)参数为方法名,和Method类似。

调用方法

通过上面的一系列操作获取到某一方法后,我们可以利用invoke()方法来调用这个方法。
测试代码:

  public String save(String name, int age) {    return "name: " + name + ", age: " + age;  }  public static void main(String[] args) throws Exception {    // 获取类对象    Class<MyReflectTest> clz = MyReflectTest.class;    // 获取对象实例    MyReflectTest reflect = (MyReflectTest) clz.newInstance();    // 获取save方法    Method method = clz.getMethod("save", String.class, int.class);    // 第一个参数为对象实例,后面的为方法的参数    Object result = method.invoke(reflect, "jack", 27);    System.out.println(result);  }

执行结果:

name: jack, age: 27

最后:反射在工作中其实用到的时候并不多,主要用来构建框架。譬如Spring中的IOC也即控制反转,其底层就是利用了反射。以后有时间再整理下这块。反射机制基础到这结束。