反射从0到入门 (6)

对于非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 的 Contructor

Constructor[] 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 +
                '}';
    }
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyfwdg.html