对于非public方法,我们可以通过 Class.getDeclaredMethod() 获取,但是调用的时候会抛出一个IllegalAccessException。为了调用非public方法,通过Method.setAccessible(true)允许其调用:
public class MethodTest5 {public static void main(String[] args) throws Exception{
Person p = new Person();
Method method = p.getClass().getDeclaredMethod("setName", String.class);
method.setAccessible(true);
method.invoke(p,"不是秃头的小李程序员");
System.out.println(p.name);
}
}
此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全。
多态如果一个Person定义了hello()方法,并且它的子类Student也重写了该方法,那么我们从Person.class获取的Method作用于Student实例时,调用的方法是哪个?
public class MethodTest6 {public static void main(String[] args) throws Exception{
// 获取Person的hello方法
Method method = Person.class.getMethod("hello");
// 对Student实例调用hello方法
method.invoke(new Student());
}
}
public class Person {
public void hello(){
System.out.println("Person:hello");
}
}
public class Student extends Person {
public void hello(){
System.out.println("Student:hello");
}
}
运行结果
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 方法
通过反射调用方法时,仍能遵循多态原则
Constructor 获取 Constructor通过 Class 实例获取所有 Constructor 的信息,Class 类提供了以下几个方法来获取方法:
- Constructor getConstructor(Class… parameterTypes):根据参数获取 public 的 ContructorConstructor[] getConstructors():获取所有 public 的 Contructor
Constructor getDeclaredConstructor(Class… parameterTypes):根据参数获取当前类的 Contructor
Constructor[] getDeclaredConstructors():获取所有当前类的 Contructor
Constructor 总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
示例如下:
public class ContructorTest1 {public static void main(String[] args) throws Exception{
Class c = Person.class;
Person p = (Person) c.newInstance();
Constructor cons1 = c.getConstructor(int.class);
Person p1 = (Person)cons1.newInstance(30);
Constructor cons2 = c.getDeclaredConstructor(String.class);
cons2.setAccessible(true);
Person p2 = (Person)cons2.newInstance("不是秃头的小李程序员");
Constructor cons3 = c.getConstructor(String.class, int.class);
Person p3 = (Person)cons3.newInstance("不是秃头的小李程序员-35",35);
}
}
Person.class
public class Person {
private String name;
private int age;
public Person() {
System.out.println("Person");
}
public Person(int age) {
this.age = age;
System.out.println("Person age:" + age);
}
private Person(String name) {
this.name = name;
System.out.println("Person name:" + name);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person toString:" + toString());
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}