JAVA - 反射

一、理解

概念

反射是JAVA的一个机制。

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

每个类被加载之后,系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。

应用

1. 获取程序在运行时刻的内部结构

这对于程序的检查工具和调试器来说,是非常实用的功能。只需要短短的十几行代码,就可以遍历出来一个Java类的内部结构,包括其中的构造方法、声明的域和定义的方法等。这不得不说是一个很强大的能力。只要有了java.lang.Class类 的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。对应的方法分别是getConstructor、getField和getMethod。这三个方法还有相应的getDeclaredXXX版本,区别在于getDeclaredXXX版本的方法只会获取该类自身所声明的元素,而不会考虑继承下来的。Constructor、Field和Method这三个类分别表示类中的构造方法、域和方法。这些类中的方法可以获取到所对应结构的元数据。

2. 在运行时刻对一个Java对象进行操作

这些操作包括动态创建一个Java类的对象,获取某个域的值以及调用某个方法。在Java源代码中编写的对类和对象的操作,都可以在运行时刻通过反射API来实现。

二、使用

取得Class对象

每个类被加载之后,系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。
在Java程序中获得Class对象通常有如下三种方式:

  1. 使用 Class 类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名)。
  2. 调用某个类的class属性来获取该类对应的 Class 对象。
  3. 调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法。
//第一种方式 通过Class类的静态方法——forName()来实现
class1 = Class.forName("com.lvr.reflection.Person");
//第二种方式 通过类的class属性
class1 = Person.class;
//第三种方式 通过对象getClass方法
Person person = new Person();
Class class1 = person.getClass();

对于方式一和方式二都是直接根据类来取得该类的 Class 对象,相比之下,方式二有如下的两种优势:

  • 代码更安全。程序在编译阶段就能够检查需要访问的 Class 对象是否存在。
  • 线程性能更好。因为这种方式无须调用方法,所以性能更好。

可以通过类的类类型创建该类的对象实例。

//Class.newInstance();
Foot foot = (Foot) c1.newInstance();

从class中获取信息(常用)

一旦获得了某个类所对应的Class 对象之后,就可以调用 Class 对象的方法来获得该对象的和该类的真实信息了。

1. 获取 Class 对应类的成员变量
Field[] getDeclaredFields(); // 获取 Class 对象对应类的所有属性,与成员变量的访问权限无关。
Field[] getFields(); // 获取 Class 对象对应类的所有 public 属性。
Field getDeclaredField(String name); // 获取 Class 对象对应类的指定名称的属性,与成员变量的访问权限无关。
Field getField(String name); // 获取 Class 对象对应类的指定名称的 public 属性。
2. 获取 Class 对应类的方法
Method[] getDeclaredMethods(); // 获取 Class 对象对应类的所有声明方法,于方法的访问权限无关。
Method[] getMethods(); // 获取 Class 对象对应类的所有 public 方法,包括父类的方法。
Method getMethod(String name, Class...parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的 public 方法。
Method getDeclaredMethod(String name, Class...parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的方法,与方法的访问权限无关。
3. 获取 Class 对应类的构造函数
Constructor[] getDeclaredConstructors(); // 获取 Class 对象对应类的所有声明构造函数,于构造函数的访问权限无关。
Constructor[] getConstructors(); // 获取 Class 对象对应类的所有 public 构造函数。
Constructor getDeclaredConstructor(Class...parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的构造函数,与构造函数的访问权限无关。
Constructor getConstructor(Class...parameterTypes); // 返回此 Class 对象对应类的、带指定形参列表的 public 构造函数。

(不常用)

4. 获取 Class 对应类的 Annotation(注释)
A getAnnotation(Class annotationClass); // 尝试获取该 Class 对对象对应类存在的、指定类型的 Annotation;如果该类型的注解不存在,则返回 null。
A getDeclaredAnnotation(Class annotationClass); // 这是Java8新增的方法,该方法尝试获取直接修饰该 Class 对象对应类、指定类型的Annotation;如果该类型的注解不存在,则返回 null。
Annotation[] getAnnotations(); // 返回修饰该 Class 对象对应类存在的所有Annotation。
Annotation[] getDeclaredAnnotations(); // 返回直接修饰该 Class 对应类的所有Annotation。
A[] getAnnotationsByType(Class annotationClass); // 获取修饰该类的、指定类型的多个Annotation。
A[] getDeclaredAnnotationsByType(Class annotationClass); // 获取直接修饰该类的、指定类型的多个Annotation。
5. 获取 Class 对应类的内部类
Class[] getDeclaredClasses(); // 返回该 Class 对象对应类包含的全部内部类。
6. 获取 Class 对应类的外部类
Class getDeclaringClass(); // 返回该 Class 对象对应类所在的外部类。
7. 获取 Class 对应类所实现的接口
Class[] getInterfaces();
8. 获取 Class 对应类所继承的父类
Class getSuperClass();
9. 获取 Class 对应类的修饰符、所在包、类名等基本信息
int getModifiers(); // 返回此类或接口的所有修饰符。修饰符由 public、protected、private、final、static、abstract 等对应的常量组成,返回的整数应使用 Modifier 工具类的方法来解码,才可以获取真实的修饰符。
Package getPackage() // 获取该类的包。
String getName() // 以字符串的形式返回此 Class 对象所表示的类的名称。
String getSimpleName() // 以字符串的形式返回此 Class 对象所表示的类的简称。
10. 判断该类是否为接口、枚举、注解类型等
boolean isAnnotation() // 返此 Class 对象是否是一个注解类型(由@interface定义)。
boolean isAnnotationPresent(Class annotationClass) // 判断此 Class 对象是否使用了Annotation修饰。
boolean isAnonymousClass() // 此 Class 对象是否是一个匿名类。
boolean isArray() //此 Class 对象是否是一个数组。
boolean isEnum() // 此 Class 对象是否是一个枚举(由 enum 关键字定义)。
boolean isInterface() // 此 Class 对象是否是一个接口(由 interface 关键字定义)。
boolean isInstance(Object obj) // 判断 obj 是否是此 Class 对象的实例。该方法完全可以替代 instanceof 操作符。

你可能感兴趣的:(JAVA - 反射)