Java反射获取方法以及调用方法

获取方法

  1. 先找到方法所在类的字节码

  2. 找到需要被获取的方法

Class类中获取方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//获取包括自身和继承(实现)过来的所有的public方法——Method不支持泛型<>,即后面不接<>
public Method[] getMethods();

//获取自身所有的方法(private、public、protected,和访问权限无关),不包括继承的
public Method[] getDeclaredMethods();

//表示获取指定的一个公共的方法,包括继承的
public Method[] getMethod(String methodName, Class<T>...parameterTypes);

参数 methodName表示获取的方法的名字
       parameterTypes表示获取的方法的参数的Class类型

//表示获取本类中的一个指定的方法(private、protected、public,与访问权限无关),不包括继承的方法
public Method[] getDeclaredMethod(String methodName, Class<T>...parameterTypes);

通过反射调用方法:

  1. 先找到方法所在类的字节码

  2. 找到需要被获取的方法

  3. 调用该方法

1
2
3
4
5
6
7
8
9
class User{

public void sayHello(){...}

public void sayHi(String naem){...}

private  void sayGoodBye(String name, int age){...}

}

如何使用反射调用一个方法?

  • 在Method类中有一个方法:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
//表示调用当前Method所表示的方法
public Object invoke(Object  obj,  Object...  args);

     参数  obj 表示被调用方法底层所属对象
             args 表示调用方法时传递的实际参数
     返回方法调用后底层方法的返回结果

Eg
public  String  sayYaString name{....}

Class<User> clz=User.class;

Method mt=clz.getMethod(sayYa, String.class);

Object obj=clz.newInstance();

Object ret=mt.invoke(obj, wili);
//要调用实例方法,必须有一个对象,方法的底层对象就是指当前Method所在的类的实例对象,
  sayHi方法具有返回值调用该方法后的返回结果使用Object接收

调用私有方法:

1
2
3
4
5
6
Method mt=clz.getDeclaredMethod(sayGoodBye, String.class, int.class);

//在调用私有方法之前,需设置该方法为可访问的权限:——否则会报错
mt.setAccessible(true);

mt.invoke(clz.newInstance(), limi, 17);

调用静态方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class User{
public static void staticMethod(){
   System.out.println(static mthod invoke.);
}
}

Eg:

Class<User> clz=User.class;
Method staticMethod=clz.getMethod(staticMthod);

两种方式调用静态方法:

  1. 因为静态方法属于所有实例对象公共的,可以创建该类的一个任意对象,通过该对象调用

    1
    2
    
    //staticMethod无参,故参数列表类型不填
    staticMethod.invoke(clz.newInstance());
  2. 如果底层方法是静态的,那么可以忽略指定的obj参数,将obj参数设置为null即可

    1
    
    staticMethod.invoke(null);

使用反射调用可变参数的方法

1
2
3
4
5
6
7
8
9
class User{
public static int  sum(int... ages){
   System.out.println(args);//打印结果可看出:可变参数底层就是一个数组
   Int sum=0;
   for(int i : args){
      Sum+=i;
   }
return sum;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static void show(String... args){...}
}

Eg:
Class<User> clz=User.class;
//可变参数底层就是一个数组
Method m=clz.getMethod(sum, int[].class);
M.invoke(null,  new int[]{1,2,3});
Method m=clz.getMethod(show, String[].class);
//会报错,可变参数是引用类型时,底层会自动解包,
  上述调用被解包后变成M.invoke(null,A,B,C);
  ——为了解决该问题我们再使用一层数组把实际参数包装起来
M.invoke(null,  new String[]{A,B,C});
//正确
M.invoke(null,  new Object[]{new String[]{A,B,C}});

通用方法:

以后在使用反射调用invoke方法时,在传递实际参数的时候,无论是基本数据类型,还是引用类型,或者是可变参数类型,把实际参数都包装在一维数组中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
m.invoke(方法的底层对象new Object[]{实际参数})

Eg
//方法参数为基本类型,且只有一个参数,解包后变成m.invoke(null,17});
m.invoke(null new Object[]{17});
//方法参数为String类型,且只有一个参数
m.invoke(null new Object[]{xxx});
//方法参数为int类型,且为可变参数或者数组类型
m.invoke(null new Object[]{new int[]{1,2}});
//方法参数为String类型,且为可变参数或者数组类型,
  new String[]{A,B}为传递的实际参数
m.invoke(null new Object[]{new String[]{A,B}});