一、什么是反射?
二、反射与正射
三、使用
一、什么是反射?
1.概念:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象.。反射就是把Java类中的各种成分映射成一个个的Java对象。
例如:
一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法,在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在于class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
类的正常加载过程:
2.好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
二、反射与正射
反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释。
一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
如:
1 Phone phone = new Phone(); //直接初始化,「正射」 2 phone.setPrice(4);
上面这样子进行类对象的初始化,我们可以理解为「正」。
而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。
这时候,我们使用 JDK 提供的反射 API 进行反射调用:
1 Class clz = Class.forName("com.xxp.reflect.Phone"); 2 Method method = clz.getMethod("setPrice", int.class); 3 Constructor constructor = clz.getConstructor(); 4 Object object = constructor.newInstance(); 5 method.invoke(object, 4);
所以说什么是反射?反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
一个简单的例子:
上面提到的示例程序,其完整的程序代码如下:
1 public class Phone { 2 private int price; 3 public int getPrice() { 4 return price; 5 } 6 public void setPrice(int price) { 7 this.price = price; 8 } 9 10 public static void main(String[] args) throws Exception{ 11 //正常的调用 12 Phone phone = new Phone(); 13 phone.setPrice(5000); 14 System.out.println("Phone Price:" + phone.getPrice()); 15 //使用反射调用 16 Class clz = Class.forName("com.xxp.api.Phone"); 17 Method setPriceMethod = clz.getMethod("setPrice", int.class); 18 Constructor phoneConstructor = clz.getConstructor(); 19 Object phoneObj = phoneConstructor.newInstance(); 20 setPriceMethod.invoke(phoneObj, 6000); 21 Method getPriceMethod = clz.getMethod("getPrice"); 22 System.out.println("Phone Price:" + getPriceMethod.invoke(phoneObj)); 23 } 24 }
从代码中可以看到我们使用反射调用了 setPrice 方法,并传递了 6000 的值。之后使用反射调用了 getPrice 方法,输出其价格。上面的代码整个的输出结果是:
1 Phone Price:5000 2 Phone Price:6000
从这个简单的例子可以看出,一般情况下我们使用反射获取一个对象的步骤:
1 //获取类的 Class 对象实例 2 Class clz = Class.forName("com.xxp.api.Phone"); 3 //根据 Class 对象实例获取 Constructor 对象 4 Constructor phoneConstructor = clz.getConstructor(); 5 //使用 Constructor 对象的 newInstance 方法获取反射类对象 6 Object phoneObj = phoneConstructor.newInstance();
而如果要调用某一个方法,则需要经过下面的步骤:
1 //获取方法的 Method 对象 2 Method setPriceMethod = clz.getMethod("setPrice", int.class); 3 //利用 invoke 方法调用方法 4 setPriceMethod.invoke(phoneObj, 6000);
到这里,我们已经能够掌握反射的基本使用。但如果要进一步掌握反射,还需要对反射的常用 API 有更深入的理解。
三、使用
1 * 获取Class对象的方式: 2 1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象 3 * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类 4 2. 类名.class:通过类名的属性class获取 5 * 多用于参数的传递 6 3. 对象.getClass():getClass()方法在Object类中定义着。 7 * 多用于对象的获取字节码的方式 8 9 * 结论: 10 同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
1 * Class对象功能: 2 * 获取功能 3 1. 获取成员变量们 4 2. 获取构造方法们 5 3. 获取成员方法们 6 4. 获取全类名 7 8 * 获取功能: 9 1. 获取成员变量们 10 * Field[] getFields() :获取所有public修饰的成员变量 11 * Field getField(String name) 获取指定名称的 public修饰的成员变量 12 13 * Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符 14 * Field getDeclaredField(String name) 15 2. 获取构造方法们 16 * Constructor<?>[] getConstructors() 17 * Constructor<T> getConstructor(类<?>... parameterTypes) 18 19 * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 20 * Constructor<?>[] getDeclaredConstructors() 21 3. 获取成员方法们: 22 * Method[] getMethods() 23 * Method getMethod(String name, 类<?>... parameterTypes) 24 25 * Method[] getDeclaredMethods() 26 * Method getDeclaredMethod(String name, 类<?>... parameterTypes) 27 4. 获取全类名 28 * String getName()
* Field:成员变量 * 操作: 1. 设置值 * void set(Object obj, Object value) 2. 获取值 * get(Object obj) 3. 忽略访问权限修饰符的安全检查 * setAccessible(true):暴力反射
* Constructor:构造方法 * 创建对象: * T newInstance(Object... initargs) * 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
* Method:方法对象 * 执行方法: * Object invoke(Object obj, Object... args) * 获取方法名称: * String getName:获取方法名