什么是反射
反射就是在程序运行的过程中,动态的获取类的信息或者动态的调用对象的方法或属性。通过反射机制,对于任意一个类,都能知道它的属性和方法,对于任意一个对象,都能调用它的任意一个属性和方法(包括私有属性和方法)。这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
我们知道,通过一个类可以实例化一个对象,而这个类本身其实也是一个对象。正如我们常说的万物皆对象。
每一个类都是 Class 类的一个实例,称之为 Class 对象。
为什么可以用使用反射
上面说到,每一个类本身也是一个对象,这个对象就是 Class 的实例,而 Class 类并没有提供 public 型的构造方法,也就是说 Class 类的对象是由 JVM 创建的。下面是 Class 类的描述。
我们使用的任何一个类(包括接口),都是 Class 类的实例。所以说,类的本质就是数据类型,没有继承关系的数据类型是无法进行赋值的。
在前面 JVM 一节中介绍到类的加载机制,Java 源文件编译后生成 class 字节码文件,当程序运行过程中需要用到某个类时,JVM 就会通过类加载器找到该类的 class 文件,根据class 文件在堆中创建一个该类的 Class 对象。这个 Class 对象指向了方法区中保存的该类的信息,即 Class 对象提供了一个访问方法区中该类的属性和方法的入口。这也就是我们能使用反射的原因。
对于每个类,我们都能找到它的 Class 对象,通过 Class 对象找到该类的属性和方法,从而实现反射机制。
如何使用反射
使用反射来获取类有三种方法:
public class TestReflection { public static void main(String[] args) throws ClassNotFoundException { // 1、通过class.forName(),需要传入类的全限定名 Class clazz1 = Class.forName("javaReflection.People"); // 2、通过类的 class 属性 Class clazz2 = People.class; // 3、通过对象的getClass() People people = new People(); Class clazz3 = people.getClass(); // clazz1、clazz2 和 clazz3都表示的是 People 这个 Class 对象,是同一个对象 System.out.println(clazz1 == clazz2 && clazz2 == clazz3); //true } }
获取到 Class 对象后,我们就可以操作这个类了。
获取构造方法
Constructor 类表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器。
getConstructors() 只能获取权限为 public 型的构造方法,而 getDeclaredConstructors() 可以获取所有类型的构造函数,包括 private、protected 等
import java.lang.reflect.Constructor; public class TestReflection { public static void main(String[] args) throws Exception { // 获取 Class 对象 Class clazz = Class.forName("javaReflection.People"); //获取所有 public 类型的构造函数 Constructor<People>[] c1 = clazz.getConstructors(); //获取 public 类型的构造函数 Constructor c2 = clazz.getConstructor(int.class); //获取所有构造函数,包括私有的构造函数 Constructor[] c3 = clazz.getDeclaredConstructors(); //该构造方法是私有的 Constructor<People> c4 = clazz.getDeclaredConstructor(String.class); } }
获取属性和方法
import java.lang.reflect.Field; import java.lang.reflect.Method; public class TestReflection { public static void main(String[] args) throws Exception { // 获取 Class 对象 Class clazz = Class.forName("javaReflection.People"); Field[] f1 = clazz.getFields(); //获取所有 public 类型的属性 Field age = clazz.getField("age"); //获取 public 类型的属性 Field[] f2 = clazz.getDeclaredFields(); //获取所有构造函数,包括私有的属性 Field name = clazz.getDeclaredField("name"); //该属性是私有的 Method m1 = clazz.getMethod("test_public"); //获取该test方法,该方法没有参数 Method m2 = clazz.getDeclaredMethod("test_private", String.class, int.class); //获取该test方法,该方法没有参数 Method[] m3 = clazz.getMethods(); //获取所有public 型的方法 Method[] m4 = clazz.getDeclaredMethods(); //获取所有方法 for(Field f : f2) { System.out.println(f); } } }
通过反射式获取属性和方法时,不包括继承父类所获得的属性以及方法。
使用反射获得的方法
import java.lang.reflect.Method; public class TestReflection { public static void main(String[] args) throws Exception { // 获取 Class 对象 Class clazz = Class.forName("javaReflect.People"); //获取方法 Method m = clazz.getMethod("test", int.class); //通过 invoke(object, param)来使用该方法。 object表示的是反射获取的类的实例, param表示该方法的参数 m.invoke(clazz.newInstance(), 15); } }
获取方法信息
获取一个类的构造函数后,我们可以调用该构造函数来实例化该类。
Class clazz = Class.forName("javaReflection.People"); Constructor<People> c = clazz.getConstructor(); People people = c.newInstance();
setAccessible
通过反射我们可以获取类的属性和方法并使用,其实是对于一个私有的属性或方法,我们虽然获得了,但却不能在类外调用它,如果在类外调用了一个私有的属性或方法,会出现 IllegalAccessException 异常。为了能在类外使用任何一个属性和方法,对于私有的属性和方法,可以通过 setAccessible() 进行设置并访问。
public class AccessibleObjectextends Object implements AnnotatedElement
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。
import java.lang.reflect.Method; public class TestReflection { public static void main(String[] args) throws Exception { // 获取 Class 对象 Class clazz = Class.forName("javaReflection.People"); //获取私有方法 Method m = clazz.getDeclaredMethod("test_private", String.class, int.class); //通过setAccessible()设置 accessible 的值为true,就可以在类外访问属性或方法 m.setAccessible(true); //通过 invoke(object, param)来使用该方法。 object表示的是反射获取的类的实例, param表示该方法的参数 m.invoke(clazz.newInstance(), "Zz-feng", 100); } }