阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

调用方法

31次阅读
没有评论

共计 3921 个字符,预计需要花费 10 分钟才能阅读完成。

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

  • Method getMethod(name, Class...):获取某个 publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有 publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

我们来看一下示例代码:

// reflection
public class Main {public static void main(String[] args) throws Exception {Class stdClass = Student.class;
        // 获取 public 方法 getScore,参数为 String:
        System.out.println(stdClass.getMethod("getScore", String.class));
        // 获取继承的 public 方法 getName,无参数:
        System.out.println(stdClass.getMethod("getName"));
        // 获取 private 方法 getGrade,参数为 int:
        System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
    }
}

class Student extends Person {public int getScore(String type) {return 99;
    }
    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 方法,需要以下代码:

// reflection
import java.lang.reflect.Method;

public class Main {public static void main(String[] args) throws Exception {// String 对象:
        String s = "Hello world";
        // 获取 String substring(int)方法,参数为 int:
        Method m = String.class.getMethod("substring", int.class);
        // 在 s 对象上调用该方法并获取结果:
        String r = (String) m.invoke(s, 6);
        // 打印调用结果:
        System.out.println(r); // "world"
    }
}

注意到 substring() 有两个重载方法,我们获取的是 String substring(int) 这个方法。思考一下如何获取 String substring(int, int) 方法。

Method 实例调用 invoke 就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。

调用静态方法

如果获取到的 Method 表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以 invoke 方法传入的第一个参数永远为 null。我们以Integer.parseInt(String) 为例:

// reflection
import java.lang.reflect.Method;

public class Main {public static void main(String[] args) throws Exception {// 获取 Integer.parseInt(String)方法,参数为 String:
        Method m = Integer.class.getMethod("parseInt", String.class);
        // 调用该静态方法并获取结果:
        Integer n = (Integer) m.invoke(null, "12345");
        // 打印调用结果:
        System.out.println(n);
    }
}

调用非 public 方法

和 Field 类似,对于非 public 方法,我们虽然可以通过 Class.getDeclaredMethod() 获取该方法实例,但直接对其调用将得到一个 IllegalAccessException。为了调用非 public 方法,我们通过Method.setAccessible(true) 允许其调用:

// reflection
import java.lang.reflect.Method;

public class Main {public static void main(String[] args) throws Exception {Person p = new Person();
        Method m = p.getClass().getDeclaredMethod("setName", String.class);
        m.setAccessible(true);
        m.invoke(p, "Bob");
        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 实例时,调用的方法到底是哪个?

// reflection
import java.lang.reflect.Method;

public class Main {public static void main(String[] args) throws Exception {// 获取 Person 的 hello 方法:
        Method h = Person.class.getMethod("hello");
        // 对 Student 实例调用 hello 方法:
        h.invoke(new Student());
    }
}

class Person {public void hello() {System.out.println("Person:hello");
    }
}

class Student extends Person {public void hello() {System.out.println("Student: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 方法;

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

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计3921字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中