Java反射技术总结
Contents
反射
概念
现在有一个对象,要通过对象找到一个类的名称,此时就要用到反射机制。
核心概念一切的操作都将使用Object完成,类,数组的引用都可以使用Object进行接收
通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。
程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
功能(重点:在运行时不是编译时)
反射主要提供了以下的功能
- 1.在运行时判断任意一个对象所属的类;
- 2.在运行时构造任意一个类的对象;
- 3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 4.在运行时调用任意一个对象的方法
用途
很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,controller之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射(把类写进配置文件中,然后通过运行时读配置文件来动态的加载类)——运行时动态加载需要加载的对象。(运行时传入类名,然后来创建对象,而不是去new 对象)(和基于接口的方法的区别: 当基于接口时,接口中的定义中方法名是固定好的,它无法直接引用到其他的方法,要加载某个对象必须重新去写new的代码,而使用接口,就可以在运行时动态的从配置文件中读取类名,动态的加载需要的类.)
java的反射机制就是增加程序的灵活性,避免将程序写死到代码里,
例如: 实例化一个 person()对象, 不使用反射, new person(); 如果想变成 实例化 其他类, 那么必须修改源代码,并重新编译。
使用反射: class.forName(“person”).newInstance(); 而且这个类描述可以写到配置文件中,如 .xml, 这样如果想实例化其他类,只要修改配置文件的”类描述”就可以了,不需要重新修改代码并编译。
1 | 反射可以结合Java的字节码,使用ASM和cglib等库,还能动态生成类。 |
认识Class类
Class是一个java类,跟Java API中定义的诸如Thread、Integer类、我们自己定义的类是一样,也继承了Object(Class是Object的直接子类)。总之,必须明确一点,它其实只是个类,只不过名字比较特殊。更进一步说,Class是一个java中的泛型类型。
Java.lang.Class是一个比较特殊的类,它用于封装被装入到JVM中的类(包括类和接口)的信息。当一个类或接口被装入的JVM时便会产生一个与之关联的java.lang.Class对象,可以通过这个Class对象对被装入类的详细信息进行访问。(Java中Class对象和类的实例对象是两个不同的概念,不能混淆!)(每一个类都有一个封装它的java.lang.Class类对象) 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象:事实上,Class对象就是用来创建类的所有的“普通”对象的。 类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(恰当地说,是被保存在一个同名的.class文件中)。在运行时,当我们想生成这个类的对象时,运行这个程序的 Java虚拟机(JVM)首先检查这个类的Class对象是否已经加载。如果尚未加载,JVM就会根据类名查找.class文件,并将其载入。 一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有(实例)对象.
在Java中Object是一切类的父类,那么所有类的对象实际上也都是Java.lang.Class类的实例,所以所有的对象都可以转变为Java.lang.Class类型表示。
如何取到Class 类对象 三种方法
1、调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。例如:
MyObject x
;
1 | Class c1 = x.getClass(); |
2、使用Class类的中静态forName()方法获得与字符串对应的Class对象。例如:
1 | Class c2=Class.forName("MyObject"),Employee必须是接口或者类的名字。 |
3、获取Class类型对象的第三个方法非常简单。如果T是一个Java类型,那么T.class就代表了匹配的类对象。例如
1 | Class cl1 = Manager.class; |
1 | //综合实例 |
Class类的常用方法
前面三种方法已经找到了Class类对象,下面就是Class类中的常用方法
1 | //传入完整包.类名实例化Class对象。!!!!!!!!!!!!!!!!!!!!!! 上面的第一种方式 |
使用class类
要想对JVM中Class类封装的信息进行访问
通过无参构造实例化对象
1 | Class<?> c =String.class;//也可以用forname得到class对象。 |
调用有参构造实例化对象
1 | 操作时需要明确调用的构造函数,并将参数进行传递。 |
反射的基本运用
反射可以用于判断任意对象所属的类,获得Class对象,构造任意一个对象以及调用一个对象。介绍一下基本反射功能的实现.
判断是否为某个类的实例
1 | public native boolean isInstance(Object obj); |
创建实例
1 使用class对象的newInstance()方法创建class对象对应类的实例
1 | Class<?> c =String.class; |
2 先通过class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法创建实例,这种方法可以用指定的构造器构造类的实例。
1 | //获取String所对应的Class对象 |
1 | //实例 |
获取方法
获取某个class对象的方法集合
1 getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
1 | public Method[] getDeclaredMethods() throws SecurityException |
2 getMethods()方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
1 | public Method[] getMethods() throws SecurityException |
3 getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
1 | public Method getMethod(String name, Class<?>... parameterTypes) |
获取构造器信息
1 | public T newInstance(Object ... initargs) |
获取类的成员变量信息
1 | getFiled: 访问公有的成员变量 |
1 | Hello hello3 =new Hello(); |
调用方法
当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为: 调用方法,必须传入对象实例
1 | public Object invoke(Object obj, Object... args) |
下面是一个实例:
1 | //实例2 |
1 | public class test1 { |
反射的应用之注解
从原理上讲,注解处理器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理。