一、概述
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。
- Java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
- 反射相关API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
二、反射的使用
使用反射前,先来介绍一下Class:
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在jvm中只有一个Class实例
- 一个Class对象对应的是一个加载到jvm中的一个.class文件
- 每个类的实例都会记得自己是由那个Class实例所生成
- 通过Class可以完整的得到一个类中的所有被加载的结构
- Class是反射的根源,任何想动态加载,运行的类,必须先获得相应的Class对象。
所以要使用反射,我们需要先获取到类的Class。首先我们先创建一个User实体类,其中包含name,age,get/set方法。
public class User {
private String name;
private int age;
public User(){}
public User(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
第一步要获取User的Class,我们先看有如下几种方式:
/*
第一种方式:已知具体的类,通过类的class属性获取。
*/
Class c1 = User.class;
/*
第二种方式:已知类的实例,通过该实例的getClass()方法获取。
*/
User user = new User();
Class c2 = user.getClass();
/*
第三种方式:已知类的全类名,通过Class的静态方法forName()获取。
*/
Class c3 = Class.forName("com.mvpapp.reflection.User");
获得Class之后,我们就可以通过Class来获取类的信息了,例如获取类的属性,方法,构造器等。
- 获取类的属性
//获得类的属性
Field[] fields = c1.getFields(); //只能获取public的属性
Field[] fields1 = c1.getDeclaredFields();//获取所有属性
for (Field field:fields1){
System.out.println(field);
}
输出:
private java.lang.String com.mvpapp.reflection.User.name
private int com.mvpapp.reflection.User.age
- 获取指定属性
//获得指定的属性值
//Field name = c1.getField("name"); //只能获取public属性
Field name2 = c1.getDeclaredField("name");//获取所有属性
System.out.println(name2);
输出:
private java.lang.String com.mvpapp.reflection.User.name
- 获取类的方法
//获取类的方法
Method[] methods = c1.getMethods();//获取本类及其父类的所有方法
Method[] methods1 = c1.getDeclaredMethods();//获取本类的所有方法
for (Method method : methods1){
System.out.println(method);
}
输出:
public java.lang.String com.mvpapp.reflection.User.toString()
public java.lang.String com.mvpapp.reflection.User.getName()
public void com.mvpapp.reflection.User.setName(java.lang.String)
public void com.mvpapp.reflection.User.setAge(int)
public int com.mvpapp.reflection.User.getAge()
- 获取指定的方法
//获取指定方法
Method get = c1.getMethod("getName",new Class[0]);
Method set = c1.getMethod("setName", String.class);
System.out.println(get);
System.out.println(set);
输出:
public java.lang.String com.mvpapp.reflection.User.getName()
public void com.mvpapp.reflection.User.setName(java.lang.String)
- 获取构造器
//获取构造器
Constructor[] constructors = c1.getConstructors();//获取public方法
Constructor[] constructors1 = c1.getDeclaredConstructors();//获取全部方法
for (Constructor constructor: constructors1){
System.out.println(constructor);
}
输出:
public com.mvpapp.reflection.User()
public com.mvpapp.reflection.User(java.lang.String,int)
- 获取指定构造器
Constructor constructor = c1.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor);
输出:
public com.mvpapp.reflection.User(java.lang.String,int)
- 通过反射创建对象
//利用反射创建对象
//1. 获得构造方法
Constructor constructor1 = c1.getDeclaredConstructor(String.class,int.class);
//2.通过构造方法创建对象
User user1 = (User) constructor1.newInstance("张三",20);
System.out.println(user1);
输出:
User{name='张三', age=20}
- 通过反射调用方法
//通过反射调用方法
//通过Class获得对象实例
User user2 = (User) c1.newInstance();
//获取set方法
Method method = c1.getMethod("setName",String.class);
//执行set方法
method.invoke(user2,"李四");
System.out.println(user2.getName());
输出:
李四
- 通过反射操作属性
//通过反射操作属性
User user3 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//因为name属性为private 所以要调用此方法更改可见性
name.setAccessible(true);
name.set(user3,"张三");
System.out.println(user3.getName());
输出:
张三
- 通过反射获取泛型
class Test {
//参数为map
public void test1(Map<String,User> map){
}
//返回为List
public List<String> test2(){
return null;
}
}
//通过反射获取泛型
1. 获取参数的泛型类型
Class test = Test.class;
//获取方法
Method method1 = test.getDeclaredMethod("test1", Map.class);
//获取泛型的参数类型
Type[] type = method1.getGenericParameterTypes();
for (Type t:type){
System.out.println(t);
//如果参数类型为参数化类型
if (t instanceof ParameterizedType){
//获取参数化类型的参数类型
Type[] actualTypeArguments = ((ParameterizedType) t).getActualTypeArguments();
for (Type actualTypeArgument: actualTypeArguments){
//打印
System.out.println(actualTypeArgument);
}
}
}
输出:
java.util.Map<java.lang.String, com.mvpapp.reflection.User>
class java.lang.String
class com.mvpapp.reflection.User
2. 获取返回值为泛型的类型
//获取返回值为泛型
//获取方法
Method method2 = test.getDeclaredMethod("test2");
//获取泛型的参数类型
Type type2 = method2.getGenericReturnType();
System.out.println(type2);
//如果参数类型为参数化类型
if (type2 instanceof ParameterizedType){
//获取参数化类型的参数类型
Type[] actualTypeArguments = ((ParameterizedType) type2).getActualTypeArguments();
for (Type actualTypeArgument: actualTypeArguments){
//打印
System.out.println(actualTypeArgument);
}
}
输出:
java.util.List<java.lang.String>
class java.lang.String
- 通过反射操作注解
在上一篇文章中Java 注解我们自定义了一个注解,这里我们就通过使用这个自定义的注解来进行示范。
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {
int id() default 0;
String type();
}
在类中声明注解
@MyAnnotation(id = 100,type = "测试")
class Test {
}
获取注解
//通过反射获取注解
Class c = Test.class;
//获取注解
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation:annotations){
System.out.println(annotation);
}
//获取注解的值
MyAnnotation annotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
System.out.println(annotation.id()+ " "+ annotation.type());
输出:
@com.mvpapp.annotation.MyAnnotation(id=100, type=测试)
100 测试
三、总结
通过上面的反射用法,我们可以动态的获取类中的属性,方法以及调用或修改这些方法和属性,可见反射的功能是很强大的。
优点:可以实现动态创建对象和编译,可以提高java程序的灵活性,允许程序创建和控制任何类的对象。
缺点:对性能有影响,反射是一种解释操作,这种操作慢与直接执行相同的操作。
应用:在日常开发中,基本都是在一些复杂的逻辑上动态的获取或操作类中的对象或方法时,但是在开发框架时,比如Retrofit请求框架,就是大量的使用注解和反射的方式实现的。