我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有Method信息。Class类提供了以下几个方法来获取Method:
Method getMethod(name, Class...):获取某个public的Method(包括父类)Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)Method[] getMethods():获取所有public的Method(包括父类)Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
---------------------------------------获得方法与获得字段是类似的----------------------------------------
示例代码:
public class fieldTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> stdclass = Class.forName("Student");
//获得public方法getScore,方法传入的参数的类型为String
System.out.println(stdclass.getMethod("getScore", String.class));
//获取继承的方法getName,无参数
System.out.println(stdclass.getMethod("getName"));
//获取private的方法getGrade,方法传入的参数的类型为String
System.out.println(stdclass.getDeclaredMethod("getGrade", int.class));
}
}
class Student extends Person{
public int getScore(String type){
return 403;
}
private int getGrade(int year){
return 1;
}
}
class Person {
public String getName() {
return "Person";
}
}
上述代码首先获取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方法,需要以下代码:
public static void main(String[] args) throws IllegalAccessException {
//String对象
String s = "Hello World";
//获取类实例
Class<? extends String> aClass = s.getClass();
//通过类实例获取类的方法(但需传入方法名,方法传入参数的类型)
Method substringMethod = aClass.getDeclaredMethod("substring", int.class);
//Method.invoke(调用的obj,传入的参数)
System.out.println(substringMethod.invoke(s, 6));
}
注意到substring()有两个重载方法,我们获取的是String substring(int)这个方法。思考一下如何获取String substring(int, int)方法。
对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
调用静态方法
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。
(因为静态方法、静态变量 是实际上存在于类当中的,而不是类的实例)(能在实例中调用该是因为编译器会帮我们自动改)
我们以Integer.parseInt(String)为例:
//获取Integer.parseInt(String )方法,参数类型为String
Method parseIntMethod = Integer.class.getDeclaredMethod("parseInt", String.class);
System.out.println(parseIntMethod);
//调用该静态方法并获取结果
Integer n = (Integer) parseIntMethod.invoke(null, "211");
System.out.println(n);
调用非public方法
和Field类似,对于非public方法,我们虽然可以通过Class.getDeclaredMethod()获取该方法实例,但直接对其调用将得到一个IllegalAccessException。为了调用非public方法,我们通过Method.setAccessible(true)允许其调用:
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person p = new Person();
Method setNameMethod = p.getClass().getDeclaredMethod("setName", String.class);
setNameMethod.setAccessible(true);
setNameMethod.invoke(p,"lg");
System.out.println(p.name);
}
class Person {
String name;
private void setName(String name) {
this.name = name;
}
}
此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全。
多态
我们来考察这样一种情况:一个Person类定义了hello()方法,并且它的子类Student也覆写了hello()方法,那么,从Person.class获取的Method,作用于Student实例时,调用的方法到底是哪个?
public class fieldTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//获取Person的hello方法
Method PERSONhello = Person.class.getDeclaredMethod("hello");
//对Student实例调用上面的hello方法
PERSONhello.invoke(new Student());
}
}
class Student extends Person{
@Override
public void hello() {
System.out.println("Student的方法hello()执行了");
}
}
class Person {
public void hello(){
System.out.println("Person的方法hello()执行");
}
}
输出结果:Student的方法hello()执行了
因此,使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。上述的反射代码:
Method m = Person.class.getMethod("hello");
m.invoke(new Student());
实际上相当于:
Person p = new Student();
p.hello();
小结
Java的反射API提供的Method对象封装了方法的所有信息:
通过Class实例的方法可以获取Method实例:getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods();
通过Method实例可以获取方法信息:getName(),getReturnType(),getParameterTypes(),getModifiers();
通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters);
通过设置setAccessible(true)来访问非public方法;
通过反射调用方法时,仍然遵循多态原则。
1060

被折叠的 条评论
为什么被折叠?



