Java|反射之调用方法

我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有Method信息。Class类提供了以下几个方法来获取Method

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • 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";
    }
}

上述代码首先获取StudentClass实例,然后,分别获取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可能不允许对javajavax开头的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方法;

通过反射调用方法时,仍然遵循多态原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以使用反射机制来调用方法。通过反射,可以在运行时获取类的信息,并动态地调用类的方法。 要调用某个方法,首先需要获取对应的Method对象。可以使用Class类的getMethod()方法或getDeclaredMethod()方法来获取Method对象。getMethod()方法只能获取公共方法,而getDeclaredMethod()方法可以获取所有方法,包括私有方法。获取Method对象后,就可以使用invoke()方法调用方法。 下面是一个示例代码,演示了如何使用反射调用方法: ```java import java.lang.reflect.Method; public class ReflectExample { public static void main(String[] args) throws Exception { // 获取Class对象 Class<?> clazz = MyClass.class; // 获取方法名和参数类型 String methodName = "myMethod"; Class<?>[] parameterTypes = {int.class, String.class}; // 获取Method对象 Method method = clazz.getDeclaredMethod(methodName, parameterTypes); method.setAccessible(true); // 如果方法是私有的,需要设置可访问 // 创建对象实例 Object obj = clazz.newInstance(); // 调用方法 Object result = method.invoke(obj, 123, "hello"); // 输出结果 System.out.println(result); } } class MyClass { private String myMethod(int num, String str) { return str + num; } } ``` 以上代码中,首先通过Class.forName()方法获取MyClass类的Class对象。然后,使用getDeclaredMethod()方法获取方法名为"myMethod",参数类型为int和String的Method对象。接着,使用newInstance()方法创建MyClass类的实例。最后,调用invoke()方法调用方法,并获取返回值。 我希望我的回答对你有帮助。如果你还有其他
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值