目录
一、什么是反射
二、Class 类
三、Class 类的一些常用方法
1、获取方法信息
2、获取属性信息
3、获取构造方法信息
四、动态调用
1、创建对象
2、修改属性
3、调用方法
4、操作动态数组
五、总结
Java 反射(Reflection)是指 Java 程序在运行时,可以动态的加载、探知、使用编译期间完全未知的类。也就是说,Java 程序可以加载一个运行时才得知类名的类,获得类的完整构造方法,并实例化出对象,给对象属性设定值或者调用对象的方法等。这种在运行时动态获取类的信息以及动态调用对象方法的功能称为 Java 反射机制
例如在使用 Eclipse 时,当开发者定义了一个类Car
,里面写了一些方法,再创建Car
类对象car
并输入car.
时,Eclipse 会弹出car
对象可用的方法给程序员选择,这些都是反射机制最常见的例子
Class
类是使用 Java 反射机制的入口,封装了一个类或接口的运行时信息,开发者通过调用 Class
类的方法可以获取这些信息。例如可以通过 Class
中的 getDeclaredMethods()
方法获取类的所有方法
下面通过一个案例来演示如何通过以上几种方式获取Class
类对象
import java.lang.reflect.*; public class TestClass{ public static void main(String[] args) { //如果将被建模的类类型未知,用Class<?>表示 Class<?> c1 = null; Class<?> c2 = null; Class<?> c3 = null; Class<?> c4 = null; Class<?> c5 = null; try{ //建议采用这种形式 c1 = Class.forName("java.lang.Object"); }catch(Exception e){ e.printStackTrace(); } c2 = new TestClass().getClass(); c3 = TestClass.class; String name = new String("大力士"); c4 = name.getClass(); c5 = name.getClass().getSuperclass(); System.out.println("Class.forName(\"java.lang.Object\") 类名称:" + c1.getName()); System.out.println("new TestClass().getClass() 类名称:" + c2.getName()); System.out.println("TestClass.class 类名称:" + c3.getName()); System.out.println("String name = \"大力士\""); System.out.println("name.getClass() 类名称:" + c4.getName()); System.out.println("name.getClass().getSuperclass() 类名称:" + c5.getName()); } }
运行结果:
Class
类的一些常用方法 Field[] getFields()
返回一个包含 Field 对象的数组,存放该类或接口的所有符合访问修饰符要求的公共属性(含继承而来的属性)。
Field[] getDeclaredFields()
返回一个包含 Field 对象的数组,存放该类或接口中 private 等四种访问修饰符修饰的所有属性(不含继承而来的属性)。可见,该方法可以突破访问修饰符的限制。
Method[] getMethods()
返回一个包含 Method 对象的数组,数组中存放的是该类及父类(或父接口)中用 public 修饰的方法。
Method[] getDeclaredMethods()
返回一个包含 Method 对象的数组,存放该类(或接口)中 private 等四种访问修饰符修饰的所有方法(不含父类或父接口中定义的方法)。可见,该方法也可以突破访问修饰符的限制。
Constructor[] getConstructors()
返回一个包含 Constructor 对象的数组,存放该类中所有用 public 修饰的公共构造方法。
Constructor getConstructor(Class[] args)
返回一个指定参数列表的 Constructor 对象。
Class[] getInterfaces()
返回一个包含 Class 对象的数组,存放该类或接口实现的接口。
T newInstance()
使用无参构造方法创建该类的一个新实例。
String getName()
以 String 的形式返回该类(类、接口、数组类、基本类型或 void)的完整名。
通过 Class
类的 getMethods()
方法、getDeclaredMethods()
方法、getMethod(String name, Class[] args)
方法和 getDeclaredMethod(String name, Class[] args)
等方法,程序员可以获得对应类的特定方法组或方法,返回值为 Method 对象数组或 Method 对象。
Class
类的 getDeclaredMethods()
方法获得了 Class
所表示的要获取的类的 private
等全部修饰符修饰的方法,但不包括继承而来的方法。使用 Class 类的 getMethods()
方法获取的是 Class 所表示的要获取的类及其父类中所有的公共方法(即 public 修饰的方法)。这就是 getDeclaredMethods()
和 getMethods()
方法的主要区别。
import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Scanner; public class TestField { public static void main(String args[]) { try { Class c = Class.forName("Sub"); Scanner input = new Scanner(System.in); System.out.print("请输入你想获取Sub类的哪个属性的类型:"); String name = input.next(); //通过指定属性名获取属性对象 Field sf = c.getDeclaredField(name); //得到属性类型 System.out.println("Sub类" + name + "属性的类型为:" + sf.getType()); System.out.println("****************************************"); //返回Field对象数组,存放该类或接口的所有属性(不包含父类或父接口中的方法) Field flist[] = c.getDeclaredFields(); System.out.println("Sub类getDeclaredFields()得到的属性如下:"); //遍历所有属性 for (int i = 0; i < flist.length; i++) { System.out.println("****************************************"); Field f = flist[i]; System.out.println("属性" + (i + 1) + "名称为:" + f.getName()); //得到属性名 System.out.println("该属性所在的类或接口为:" + f.getDeclaringClass()); System.out.println("该属性的类型为:" + f.getType()); //得到属性类型 //以整数形式返回由此Field对象表示的属性的Java访问权限修饰符 int m = f.getModifiers(); //使用Modifier类对表示访问权限修饰符的整数进行解码显示 System.out.println("该属性的修饰符为:" + Modifier.toString(m)); } } catch (Exception e) { e.printStackTrace(); } } }
通过 Class 类,可以获得属性和方法,但是 getDeclaredMethods()
和 getMethods()
等获取方法的方法只能获得普通方法,不能获得类的构造方法。接下来,通过 Class
类的 getConstructors()
方法和 getDeclaredConstructors()
方法,可以获得对应类的构造方法,返回值为 Constructor 对象数组
通过 Class
类的方法获取了对应类的属性、方法和构造方法的详细信息,但反射的意义远不止如此。接下来,将通过之前获取的属性、方法和构造方法的详细信息,来动态创建对象、修改属性和调用方法。
可使用 newInstance()
方法和使用 newInstance(Object[] args)
方法两种方式实例化对象
到目前为止,可以看出点反射机制的作用—可以根据用户运行时输入的信息,动态创建不同的对象,再调用对象的方法执行相关的功能
通过 Class 类的 newInstance()
方法创建对象,该方法要求该 Class 对应类有无参构造方法。执行 newInstance()
方法实际上就是使用对应类的无参构造方法来创建该类的实例,其代码的作用等价于Super sup = new Super();
如果要想使用有参构造方法创建对象,则需要先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance(Object[] args)
方法来创建该 Class 对象对应类的实例
需要注意的是,通过 Class 对象获得指定 Constructor 对象的方法 getDeclaredConstructor((Class[] args))
中,参数列表为 Class 类数组。通过 Constructor 的 newInstance()
方法,也可以创建无参对象,只要在调用 getDeclaredConstructor((Class[] args))
和 newInstance(Object[] args)
方法时,参数列表为空即可。
import java.lang.reflect.*; public class TestChangeField{ public static void main(String args[]) { try { Class c = Class.forName("Super2"); Super2 sup = (Super2)c.newInstance(); //通过属性名获得Field对象 Field f = c.getDeclaredField("supPri");//supPri为私有属性 //取消属性的访问权限控制,即使private属性也可以进行访问 f.setAccessible(true); //调用get(Object o)方法取得对象o对应属性值 System.out.println("取消访问权限控制后访问supPri,其值为:" + f.get(sup)); //调用set(Object o,Object v)方法设置对象o对应属性值 f.set(sup, 20); System.out.println("f.set(sup, 20)后访问supPri,其值为:" + f.get(sup)); } catch (Exception e) { e.printStackTrace(); } } }
使用反射机制,通过调用 Method 类的一些方法,动态执行 Class 对应类的方法
假设现在有这样的需求,需要在程序运行时,根据用户提供的不同参数列表(方法名称、参数个数、参数类型),动态调用不同的方法完成不同的功能。
import java.lang.reflect.*; public class TestInvokeMethod { public int add(int x, int y) { return x + y; } public int add(int x) { return x + 1; } public int multiply(int x, int y) { return x * y; } public int multiply(int x) { return x * x; } public static void main(String args[]) { try { Class c = TestInvokeMethod.class; Object obj = c.newInstance(); //通过方法名、参数类型列表,获得Method对象 Method m = c.getDeclaredMethod("multiply", new Class[]{int.class, int.class}); //invoke(Object o,Object[] args)方法调用对象o对应方法 System.out.println("调用方法:multiply,输入值为int型3和4,结果为:" + m.invoke(obj, new Object[]{3, 4})); Method m2 = c.getDeclaredMethod("add", new Class[]{int.class}); System.out.println("调用方法:add,输入值为int型18,结果为:" + m2.invoke(obj, new Object[]{18})); } catch (Exception e) { e.printStackTrace(); } } }
Java 在创建数组的时候,需要指定数组长度,且数组长度不可变。而 java.lang.reflect
包下提供了一个 Array
类,这个类中包括一系列 static
方法,通过这些方法可以创建动态数组,对数组元素进行赋值、取值操作
Array 类提供的主要方法(均为静态方法)如下。
Object newInstance(Class componentType, int length)
创建一个元素类型为 componentType、长度为 length 的新数组。
Object newInstance(Class componentType, int... dimensions)
创建一个元素类型为 componentType、长度为 length、维度为 dimensions 的多维数组。
void setXxx(Object array, int index,xxx val)
将数组对象 array 中索引元素的值设置为指定的 xxx 类型的 val 值。
xxx getXxx(Object array, int index)
获取数组对象 array 中索引为 index 的数组元素值。
getMethods()
只能获取 public 修饰的方法,但这些方法既可以是本类中定义的、也可以在父类(或父接口)中定义的;getDeclaredMethods()
可以获取 private 等四种访问修饰符修饰的方法,但这些方法只能是在本类定义的,不包含父类(或父接口)中定义的方法。getDeclaredFields()
与 getFields()
,及 getDeclaredConstructors()
和 getConstructors()
方法的区别与之类似;setAccessible(true)
;Array.newInstance()
方法来创建数组对象。