java动态代理 反射调用方法对象

正常java获得一个对象是通过反射 现在我们用Class来获得对象

创建类的对象:调用class对象的newInstance()方法

并且类必须有一个无参构造器

//动态对象,通过反射
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //获得class对象
        Class c1 = Class.forName("Dome3.Person");

        //构造一个对象
        Person person = (Person) c1.newInstance();//调用的是无参构造器
        System.out.println(person);

        
    }
}
class Person{
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person() {

    }
   

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Person{name=‘null’} 输出结果可以看到调用的是无参构造器,如果想调用有参构造器怎么办呢?

可以通过Class类的getDeclaredConstructor()取得本类指定的形参类型的构造器

向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。通过Constructor实例化对象

//动态对象,通过反射
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //获得class对象
        Class c1 = Class.forName("Dome3.Person");

        //构造一个对象
//        Person person = (Person) c1.newInstance();//调用的是无参构造器
//        System.out.println(person);

        //通过构造器来创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
        Person person1 = (Person) constructor.newInstance("li", 20);
        System.out.println(person1);
    }
}

可以看到通过构造器可以调用对象的有参构造器

通过反射来调用普通方法

通过反射,调用类中的方法,通过Method类完成。
通过Class类的getDeclaredMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

//通过反射调用普通方法

Person c3 = (Person) c1.newInstance();

//通过反射获取一个方法
Method name = c1.getDeclaredMethod("setName", String.class);
//invoke(对象,方法的值)
name.invoke(c3,"无敌的我又回来了");
System.out.println(c3.getName());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8eRNOHmF-1659068260546)(C:\Users\Lizhenhai\AppData\Roaming\Typora\typora-user-images\image-20220729113855933.png)]

通过反射来操作属性

//通过反射操作属性
Person c4 = (Person) c1.newInstance();
Field name1 = c1.getDeclaredField("name");
name1.setAccessible(true);//因为name属性私有,所以需要赋予操作权限,就可以操作私有属性
name1.set(c4, "无敌");
System.out.println(c4.getName());

setAccessible

Method和Field、Constructor对象都有setAccessible()方法。setAccessible作用是启动和禁用访问安全检查的开关。
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。使得原本无法访问的私有成员也可以访问
参数值为false则指示反射的对象应该实施Java语言访问检查

下面来做一下性能对比分析

public class Test03 {
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        test1();
        test2();
        test3();
    }
    //反射方式调用
    public static void test1() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Person person =  new Person();
        Class c1 = person.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        //设置时间点
        long startTime = System.currentTimeMillis();//获取开始时间

        for (int i = 0; i < 100000000; i++) {
            getName.invoke(person,null);
        }

        long endTime = System.currentTimeMillis();//获取结束时间
        System.out.println("反射调用方法执行1亿次"+(endTime-startTime)+"ms");
    }
    //反射方式调用 关闭检测
    public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Person person =  new Person();
        Class c1 = person.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);

        getName.setAccessible(true);
        //设置时间点
        long startTime = System.currentTimeMillis();//获取开始时间

        for (int i = 0; i < 100000000; i++) {
            getName.invoke(person,null);
        }

        long endTime = System.currentTimeMillis();//获取结束时间
        System.out.println("反射调用方法关闭检测执行1亿次"+(endTime-startTime)+"ms");
    }

    //普通方法调用
    public static void test3(){

        Person person =  new Person();

        //设置时间点
        long startTime = System.currentTimeMillis();//获取开始时间

        for (int i = 0; i < 100000000; i++) {
            person.getName();
        }

        long endTime = System.currentTimeMillis();//获取结束时间
        System.out.println("普通调用方法执行1亿次"+(endTime-startTime)+"ms");
    }

}

可以看到普通调用方法是最节约资源的

如果平时反射调用多可以建议关闭检测提升效率