调用方法
我们已经能通过Class
实例获取所有Field
对象,同样的,可以通过Class
实例获取所有Method
信息。Class
类提供了以下几个方法来获取Method
:
Method getMethod(name, Class…)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class…)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
我们来看一下示例代码:
上述代码首先获取Student
的Class
实例,然后,分别获取public
方法、继承的public
方法以及private
方法,打印出的Method
类似:
public int Student.getScore(java.lang.String)
public java.lang.String Person.getName()
private int Student.getGrade(int)
一个Method
对象包含一个方法的所有信息:
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个Class实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义。
调用方法
当我们获取到一个Method
对象时,就可以对它进行调用。我们以下面的代码为例:
String s = "Hello world";
String r = s.substring(6); // "world"
如果用反射来调用substring
方法,需要以下代码:
注意到substring()
有两个重载方法,我们获取的是String substring(int)
这个方法。思考一下如何获取String substring(int, int)
方法。
对Method
实例调用invoke
就相当于调用该方法,invoke
的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
调用静态方法
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke
方法传入的第一个参数永远为null
。我们以Integer.parseInt(String)
为例:
调用非public方法
和Field类似,对于非public方法,我们虽然可以通过Class.getDeclaredMethod()
获取该方法实例,但直接对其调用将得到一个IllegalAccessException
。为了调用非public方法,我们通过Method.setAccessible(true)
允许其调用:
此外,setAccessible(true)
可能会失败。如果JVM运行期存在SecurityManager
,那么它会根据规则进行检查,有可能阻止setAccessible(true)
。例如,某个SecurityManager
可能不允许对java
和javax
开头的package
的类调用setAccessible(true)
,这样可以保证JVM核心库的安全。
多态
我们来考察这样一种情况:一个Person
类定义了hello()
方法,并且它的子类Student
也覆写了hello()
方法,那么,从Person.class
获取的Method
,作用于Student
实例时,调用的方法到底是哪个?
运行上述代码,发现打印出的是Student:hello
,因此,使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。上述的反射代码:
Method m = Person.class.getMethod("hello");
m.invoke(new Student());
实际上相当于:
Person p = new Student();
p.hello();
练习
利用反射调用方法:下载练习:reflect-method (推荐使用IDE练习插件快速下载)
小结
Java的反射API提供的Method对象封装了方法的所有信息:
通过Class
实例的方法可以获取Method
实例:getMethod()
,getMethods()
,getDeclaredMethod()
,getDeclaredMethods()
;
通过Method
实例可以获取方法信息:getName()
,getReturnType()
,getParameterTypes()
,getModifiers()
;
通过Method
实例可以调用某个对象的方法:Object invoke(Object instance, Object… parameters)
;
通过设置setAccessible(true)
来访问非public
方法;
通过反射调用方法时,仍然遵循多态原则。