Java 反射Reflection总结二

一、简介

上一篇文章总结了一些如何使用反射机制获取类的字段、方法以及构造方法信息的方法;本节主要总结如何通过反射动态调用类的方法、动态创建对象以及一些示例等。

 

二、反射使用示例


首先,本节的User类需要做一些修改,主要加入了一些静态属性以及静态方法,后面会用到,具体代码如下:

public class User {
    private int id;
    private String name;
    private int age;

    /**
     * 静态属性
     */
    private static String flag;

    /**
     * 静态方法
     *
     * @param name
     * @return
     */
    public static String sayHello(String name) {
        return name;
    }

    public User() {
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static String getFlag() {
        return flag;
    }

    public static void setFlag(String flag) {
        User.flag = flag;
    }

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

【a】动态调用构造方法创建对象

  • 底层调用的是无参构造方法,所以无参构造方法必须显式声明
  • clazz.newInstance() : 通过调用Class对象的newInstance()方法创建对象
  • constructor.newInstance(Object ... initargs) : 通过调用constructor的newInstance()方法创建对象
/**
 * @Description: 动态调用构造方法
 * @author: weishihuai
 * @Date: 2018/12/19 16:00
 */
public class DynamicInvokeConstructor {
    public static void main(String[] args) {
        Class<?> clazz;
        try {
            clazz = Class.forName("com.wsh.reflection03.User");

            /**
             * 动态调用构造方法创建对象:
             * 1. 底层调用的是无参构造方法,所以无参构造方法必须显式声明
             * 2. clazz.newInstance() : 通过调用Class对象的newInstance()方法创建对象
             * 3. constructor.newInstance(Object ... initargs) : 通过调用constructor的newInstance()方法创建对象
             */

            User user = (User) clazz.newInstance();
            System.out.println(user);

            Constructor constructor = clazz.getDeclaredConstructor(int.class, String.class, int.class);
            User user1 = (User) constructor.newInstance(1, "zhangsan", 20);
            System.out.println(user1);

        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

注意: 无参构造方法必须要显式声明,否则会报错:

 

【b】动态调用普通方法/静态方法

  • method.invoke(Object obj, Object... args) : obj-调用的实体对象 args:参数值

如果是调用静态方法,那么使用:

  • method.invoke(null, Object... args) : obj-null(因为静态方法不属于某个对象,属于整个类,传入null即可)  args:参数值
/**
 * @Description: 动态调用普通方法/静态方法
 * @author: weishihuai
 * @Date: 2018/12/19 16:00
 */
public class DynamicInvokeMethod {
    public static void main(String[] args) {
        Class<?> clazz;
        try {
            //通过反射获取User类Class对象
            clazz = Class.forName("com.wsh.reflection03.User");

            /**
             * 通过反射操作普通方法:
             * method.invoke(Object obj, Object... args) : obj-调用的实体对象  args:参数值
             */
            User user2 = (User) clazz.newInstance();
            Method method = clazz.getDeclaredMethod("setName", String.class);
            Method method1 = clazz.getDeclaredMethod("setAge", int.class);
            method.invoke(user2, "lisi");
            method1.invoke(user2, 18);
            System.out.println(user2);

            Method method2 = clazz.getDeclaredMethod("getName");
            method2.invoke(user2);
            System.out.println(user2.getName());

            /**
             * 获取静态方法
             */
            Method staticMethod = clazz.getDeclaredMethod("sayHello", String.class);
            Object o = staticMethod.invoke(null, "zhangsan");
            System.out.println("动态调用静态方法,返回值-->" + o);

        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

 

【c】动态操作字段信息

  • field.set(Object obj, Object value): field: 某个字段 obj:实体对象 value:字段值
  • field.get(Object obj): field: 某个字段 obj:实体对象

如果操作的是静态属性,那么使用:

  • field.set(null, Object value): field: 某个字段   value:字段值
  • field.get(null):    field: 某个字段 
/**
 * @Description: 动态操作字段信息
 * @author: weishihuai
 * @Date: 2018/12/19 16:00
 */
public class DynamicInvokeField {
    public static void main(String[] args) {
        Class<?> clazz = null;
        try {
            clazz = Class.forName("com.wsh.reflection03.User");

            /**
             * 通过反射API操作属性:
             * field.set(Object obj, Object value):    field: 某个字段   obj:实体对象   value:字段值
             * field.get(Object obj):   field: 某个字段  obj:实体对象
             */
            User user3 = (User) clazz.newInstance();
            Field field = clazz.getDeclaredField("name");

//            //不做安全检查,直接操作属性
            field.setAccessible(true);
            field.set(user3, "wangwu");
//            //wangwu
            System.out.println(user3.getName());
//            //wangwu
            System.out.println(field.get(user3));

            /**
             * 获取静态属性
             */
            Field staticField = clazz.getDeclaredField("flag");
            staticField.setAccessible(true);
            staticField.set(null, "flag");
            System.out.println("静态属性flag的值为-->" + staticField.get(null));

        } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

注意,不能直接访问私有属性或者私有构造方法以及私有方法,必须设置 field.setAccessible(true); 跳过安全检查才能访问私有属性、方法以及构造方法。

如果没有设置跳过安全检查直接访问的话,会直接报错,如下代码:

public class DynamicInvokeField {
    public static void main(String[] args) {
        Class<?> clazz = null;
        try {
            clazz = Class.forName("com.wsh.reflection03.User");

            /**
             * 通过反射API操作属性:
             * field.set(Object obj, Object value):    field: 某个字段   obj:实体对象   value:字段值
             * field.get(Object obj):   field: 某个字段  obj:实体对象
             */
            User user3 = (User) clazz.newInstance();
            Field field = clazz.getDeclaredField("name");

//            不能访问私有属性
            field.set(user3, "wangwu");
            System.out.println(user3.getName());

        } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

以上就是关于如何通过反射动态操作构造方法、属性、普通方法以及静态方法,下面我们总结一些使用反射的示例:

【d】使用反射跳过泛型检查
大家都知道,泛型检查只是在编译的时候才存在的,对于编译好的Class字节码不存在泛型检查,所以我们可以利用反射获取的Class对象跳过泛型的检查。

/**
 * @Description: 反射示例一: 绕过泛型检查
 * @author: weishihuai
 * @Date: 2018/12/19 16:06
 */
public class ReflectionExample01 {
    public static void main(String[] args) {

        List<String> namesList = new ArrayList<>();
        namesList.add("zhangsan");
        namesList.add("lisi");

//        编译器报错,不满足泛型类型要求
//        namesList.add(123);

        System.out.println("改变之前-->" + namesList);

        //通过反射获取List的Class对象
        Class<? extends List> clazz = namesList.getClass();
        try {
            Method method = clazz.getDeclaredMethod("add", Object.class);
            method.invoke(namesList, 123);

            System.out.println("改变之后-->" + namesList);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

【e】反射操作注解

我们可以通过反射,获取到类上面的注解以及字段甚至方法上面的注解,拿到这些注解的内容以及属性,可以进一步操作。下面的示例是通过反射获取注解中标注的内容信息。

自定义注解Mytable.java

@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyTable {
    /**
     * 数据表名称
     */
    String value();
}

自定义注解MyField.java

@Target(ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyField {
    /**
     * 列名称
     */
    String columnName();

    /**
     * 数据类型
     */
    String type();

    /**
     * 长度
     */
    int length();

}

Student.java:

@MyTable(value = "tb_student")
public class Student {
    @MyField(columnName = "sname", type = "varchar2", length = 128)
    private String name;
    @MyField(columnName = "age", type = "int", length = 3)
    private int age;
    @MyField(columnName = "id", type = "int", length = 128)
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

测试:

public class Demo {
    public static void main(String[] args) {

        try {
            //通过反射获取Student类对象
            Class clazz = Class.forName("com.wsh.annotation02.Student");

            //获取Student类的所有有效注解
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                //@com.wsh.annotation02.MyTable(value=tb_student)
                System.out.println(annotation);
            }

            //获取类指定的注解
            MyTable myTable = (MyTable) clazz.getAnnotation(MyTable.class);
            System.out.println(myTable.value());  //tb_student

            //获取类的属性的注解
            Field name_field = clazz.getDeclaredField("name");
            MyField nameField = name_field.getAnnotation(MyField.class);
            System.out.println(nameField.columnName());   //sname
            System.out.println(nameField.type());  //varchar2
            System.out.println(nameField.length());  //128

            //至此,各个字段的属性名称、长度、类型都获取了、就可以动态拼接创建数据表的sql...

        } catch (ClassNotFoundException | NoSuchFieldException e) {
            e.printStackTrace();
        }

    }
}

如上图,成功获取到注解中标注的内容信息,我们进而可以进行下一步的包装处理等。

 

三、总结

本文是笔者对反射机制之动态操作构造方法、属性、方法的一些总结以及示例,通过两篇文章的总结,相信可以对反射有一个比较深刻的理解,并且可以掌握反射常用的一些使用方法。仅供大家参考,希望能对大家有所帮助。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值