获取构造方法
一般最常用的就是下面对应的四种方式,带有declared关键字的可以获取到所有的构造方法,private修饰符修饰的也不例外,反之,只能获取public修饰符修饰非构造方法。
Constructor getConstructor(Class… parameterTypes)
Constructor[] getConstructors()
Constructor getDeclaredConstructor(Class… parameterTypes)
Constructor[] getDeclaredConstructors()
Class clazz = User.class; Constructor[] constructors = clazz.getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { System.out.print(Modifier.toString(constructors[i].getModifiers()) + " "); System.out.print(constructors[i].getName() + "("); Class[] paramTypes = constructors[i].getParameterTypes(); for (int j = 0; j < paramTypes.length; j++) { if (j < paramTypes.length - 1) { System.out.print(paramTypes[j].getSimpleName() + " args_" + j + ","); } else { System.out.print(paramTypes[j].getSimpleName() + " args_" + j); } } System.out.println(")"); } //public com.yimi.demo.bean.User(String args_0,String args_1,int args_2) //private com.yimi.demo.bean.User(String args_0) //public com.yimi.demo.bean.User() Class clazz = User.class; Constructor[] constructors = clazz.getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { System.out.print(Modifier.toString(constructors[i].getModifiers()) + " "); System.out.print(constructors[i].getName() + "("); Class[] paramTypes = constructors[i].getParameterTypes(); for (int j = 0; j < paramTypes.length; j++) { if (j < paramTypes.length - 1) { System.out.print(paramTypes[j].getSimpleName() + " args_" + j + ","); } else { System.out.print(paramTypes[j].getSimpleName() + " args_" + j); } } System.out.println(")"); } //public com.yimi.demo.bean.User(String args_0,String args_1,int args_2) //private com.yimi.demo.bean.User(String args_0) //public com.yimi.demo.bean.User() |
打印出来的方法可以看出,在获取所有Constructor数组列表中,构造方法的打印顺序是参数最多的索引最靠前。在获取权限修饰符的时候constructors[i].getModifiers(),默认返回的是一个整形数字,从方法名getModifiers也可以看出来这个值不单单是一个修复符这么简单,所以末尾加了一个s,实际上获取到的返回值是所有权限修饰符之和,如果是public static final,返回值就是这三个修饰符对应的整数之和,当我们调用Modifier.toString()方法的时候回自动返回对应字符串修饰符。
constructors[i].getParameterTypes()会返回构造方法中所有参数对应的Class数组,这样就可以获取到所有参数对应的数据类型。
如果想要获取带有某种参数类型的构造方法可以使用传入不定长参数的方法getConstructor(Class… parameterTypes),该方法返回的是单个构造方法。
注:本文示例是基于java version “1.8.0_111″,在不定长参数处理上面好像跟以前不一样了,在该版本中不定长参数必须要以数组的形式传入,如果没有参数也必须传入一个null。
获取属性
类似于上面的获取构造方法,包括后面获取方法也是采用同样的命名方式,只要带有declared关键字的方法都是获取所有在类中声明的相关属性,否则获取到的是只有使用public修饰符修饰的属性。
Field getField(String name); //返回对应name的public属性
Field[] getFields(); //所有public属性
Field getDeclaredField(String name);
Field[] getDeclaredFields();
Class clazz=User.class; Field[] fields=clazz.getDeclaredFields(); for(int i=0;i<fields.length;i++){ System.out.print(Modifier.toString(fields[i].getModifiers())+" "); System.out.print(fields[i].getType().getSimpleName()+" "); System.out.println(fields[i].getName()); } //private static final long serialVersionUID //private String id //public String username //private int age Class clazz=User.class; Field[] fields=clazz.getDeclaredFields(); for(int i=0;i<fields.length;i++){ System.out.print(Modifier.toString(fields[i].getModifiers())+" "); System.out.print(fields[i].getType().getSimpleName()+" "); System.out.println(fields[i].getName()); } //private static final long serialVersionUID //private String id //public String username //private int age |
获取方法
使用反射获取方法跟上面两种类似。
Method getMethod(String name, Class… parameterTypes);
Method[] getMethods(); //所有public方法
Method getDeclaredMethod(String name, Class… parameterTypes);
Method[] getDeclaredMethods(); //所有方法
Class clazz=User.class; Method[] methods=clazz.getDeclaredMethods(); for(int i=0;i<methods.length;i++){ System.out.print(Modifier.toString(methods[i].getModifiers())+" "); System.out.print(methods[i].getReturnType().getSimpleName()+" "); System.out.print(methods[i].getName()+"("); Class[] paramTypes = methods[i].getParameterTypes(); for (int j = 0; j < paramTypes.length; j++) { if (j < paramTypes.length - 1) { System.out.print(paramTypes[j].getSimpleName()+" args_"+j+ ","); } else { System.out.print(paramTypes[j].getSimpleName()+" args_"+j); } } System.out.println(")"); } //public String toString() //public void sayHello(String args_0) //public void sayHello() //public static void printName() //private void printId() Class clazz=User.class; Method[] methods=clazz.getDeclaredMethods(); for(int i=0;i<methods.length;i++){ System.out.print(Modifier.toString(methods[i].getModifiers())+" "); System.out.print(methods[i].getReturnType().getSimpleName()+" "); System.out.print(methods[i].getName()+"("); Class[] paramTypes = methods[i].getParameterTypes(); for (int j = 0; j < paramTypes.length; j++) { if (j < paramTypes.length - 1) { System.out.print(paramTypes[j].getSimpleName()+" args_"+j+ ","); } else { System.out.print(paramTypes[j].getSimpleName()+" args_"+j); } } System.out.println(")"); } //public String toString() //public void sayHello(String args_0) //public void sayHello() //public static void printName() //private void printId() |
反射简单使用
创建对象
如果创建一个默认无参的对象一般直接使用Class实例的newInstance()方法就可以了,创建过程如下:
Class clazz=Class.forName("com.yimi.demo.bean.User"); | User user=(User) clazz.newInstance(); |
但是如果需要创建一个带有构造参数的对象,使用Class来创建就不够用了,这时候我们还需要使用Constructor构造方法来进行创建。
Class clazz=Class.forName("com.yimi.demo.bean.User"); Constructor constructor=clazz.getConstructor(new Class[]{String.class,String.class,int.class}); User user=(User) constructor.newInstance(new Object[]{"1001","admin",new Integer(20)}); |
如果需要调用私有构造方法,一定要使用带有declared关键字的方法getDeclaredConstructor(),同时记住必须使用setAccessible(true),否则就会抛出 java.lang.IllegalAccessException异常。一般情况下调用带有declared关键字方法,都要随后设置一下setAccessible(true),这几乎就是使用反射的模板语句。
setAccessible()方法提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
创建方法调用
首先看一个调用私有无参方法,使用反射真正调用一个方法实际上是用的invoke()方法,下面是使用反射的方式调用User类的私有printId()方法。
Class clazz=Class.forName("com.yimi.demo.bean.User"); Method method=clazz.getDeclaredMethod("printId",null); method.setAccessible(true); method.invoke(clazz.newInstance(),null); |
invoke(Object obj, Object... args)有两个参数。第一个参数是方法定义所在类的实例,第二个是方法的入参。
下面是使用反射调用需要传参的方法sayHello()。
Class clazz=Class.forName("com.yimi.demo.bean.User"); Method method=clazz.getMethod("sayHello",new Class[]{String.class}); method.invoke(clazz.newInstance(),new String[]{"admin"}); |
创建属性调用
这种情况一般是某个类的属性在外部不可访问,但是某些情况下有需要重新设定一下,这时候就可以使用反射来进行重新赋值。
Java
Class clazz=User.class; Object obj=clazz.newInstance(); Field field=clazz.getDeclaredField("id"); field.setAccessible(true); field.set(obj, "1001"); System.out.println(field.get(obj)); System.out.println(field.get(obj)); |
反射中Field相对还是比较常见的一种使用,所以它还提供了许多针对基本数据类型的方法如setDouble()、setBoolean()、setInt()等等。
本文针对反射只是一些很基础的部分,有关反射更多内容后续会继续介绍如操作泛型以及注解。