一、简介
上一篇文章总结了一些如何使用反射机制获取类的字段、方法以及构造方法信息的方法;本节主要总结如何通过反射动态调用类的方法、动态创建对象以及一些示例等。
二、反射使用示例
首先,本节的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();
}
}
}
如上图,成功获取到注解中标注的内容信息,我们进而可以进行下一步的包装处理等。
三、总结
本文是笔者对反射机制之动态操作构造方法、属性、方法的一些总结以及示例,通过两篇文章的总结,相信可以对反射有一个比较深刻的理解,并且可以掌握反射常用的一些使用方法。仅供大家参考,希望能对大家有所帮助。