反射
反射机制主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。适用于计算机科学领域关于应用反射性的研究。本文简单整理了一些关于反射的相关问题。
原文链接:全网最完整Java学习笔记
基本介绍
反射:在程序运行期间动态地获取类的信息并对类进行操作的机制。在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
通过反射机制可以实现:
获取类或对象的Class对象:程序运行时,可以通过反射获得任意一个类的Class对象,并通过这个对象查看这个类的所有方法和属性(包括私有,私有需要给该字段调用
setAccessible(true)方法开启私有权限)。注意类的class对象是运行时生成的,类的class字节码文件是编译时生成的。创建实例:程序运行时,可以利用反射先创建类的Class对象再创建该类的实例,并访问该实例的成员;Xxx.class.newInstance() ;例如在Spring容器类源码里,Bean实例化就是通过Bean类的Class对象。Bean类的Class对象是从BeanDefinition对象的type成员变量取的。BeanDefinition对象存储一些Bean的类型、名称、作用域等声明信息。
生成动态代理类或对象:程序运行时,可以通过反射机制生成一个类的动态代理类或动态代理对象。例如JDK中Proxy类的newProxyInstance静态方法,可以通过它创建基于接口的动态代理对象。
类的字节码文件和Class对象的区别:
- 类的class字节码文件是编译时生成的,类的class对象是运行时生成的。
 - 类的字节码文件是存储在电脑硬盘中的文件,如Test.class;类的Class对象是存放在内存中的数据,可快速获取其中的信息;
 - 两者都存储类的各种信息;
 
获取类Class对象的JVM底层:如果该类没有被加载过,会首先通过JVM实现类的加载过程,即加载、链接(验证、准备、解析)、初始化,加载阶段会生成类的Class对象。
获取类Class对象的方法:dog.getClass();,Dog.class;,Class.forName("package1.Dog");
特点:
- 访问私有成员:构造方法、成员变量、方法对象取消访问检查可以访问私有成员;public void setAccessible(boolean flag):值为true,取消访问检查
 - 越过泛型检查:反射可以越过泛型检查,例如在ArrayList
中添加字符串  
反射的优缺点:
- 优点:
- 运行时获取属性:运行期间能够动态的获取类,提高代码的灵活性。
 - 访问私有成员:构造方法、成员变量、方法对象取消访问检查可以访问私有成员;public void setAccessible(boolean flag):值为true,取消访问检查
 - 越过泛型检查:反射可以越过泛型检查,例如在ArrayList
中添加字符串  
 - 缺点:性能差。性能比直接的Java代码要差很多。
 
应用场景:
- JDBC加载数据库的驱动:使用JDBC时,如果要创建数据库的连接,则需要先通过反射机制加载数据库的驱动程序;
 - Bean的生命周期:
- 实例化xml解析出的类:多数框架都支持注解或XML配置来定义应用程序中的类,从xml配置中解析出来的类是字符串,需要利用反射机制实例化;如Spring通过
<bean id="xx" class="类全限定名">和<property name="按名称注入" ref="被注入Bean的id">定义bean,然后通过Class.forName("xxx.Xxx")获取类的class对象,然后创建实例。 - 注解容器类加载Bean、实例化Bean:Bean的生命周期中,注解容器类的构造方法会遍历
@ComponentScan("扫描路径")下的.class文件,通过类加载器.load("类名")方式获得类的class对象,存入beanDefinitionMap。然后遍历beanDefinitionMap,通过class对象实例化等。 
 - 实例化xml解析出的类:多数框架都支持注解或XML配置来定义应用程序中的类,从xml配置中解析出来的类是字符串,需要利用反射机制实例化;如Spring通过
 - AOP创建动态代理对象:面向切面编程(AOP)的实现方案,是在程序运行时创建目标对象的代理对象,这必须由反射机制来实现。
 
验证反射可以绕过泛型检查:
基于反射,我们可以给ArrayList<Integer>对象中,加入字符串 
1  | public class Test {  | 
反射获取Class对象
基本介绍
Class类的对象:程序运行时,可以通过反射获得任意一个类的Class对象,并通过这个对象查看这个类的所有方法和属性(包括私有,私有需要给该字段调用setAccessible(true)方法开启私有权限)。
注意类的class对象是运行时生成的,类的class字节码文件是编译时生成的。
获取类Class对象:
- 对象.getClass():Object是一切类的根类,Object类有个getClass()方法可以获取类的Class对象。例如dog.getClass();
 - 类名.class(推荐):例如Dog.class;
 - Class.forName(“类名”):例如Class.forName(“package1.Dog”);
 
Class对象的常用方法:
- 获取类的信息:
- String getName():返回类的全限定名。全限定名包含包名和类名,用于唯一标识类或接口。例如package1.Dog、java.lang.String、java.util.Map$Entry
 - String getSimpleName():返回类的简单名。例如Dog
 - tring getCanonicalName():返回类的规范名。规范名是类的规范字符串形式,常用于打印和日志记录。例如package1.Dog、java.lang.String、java.util.Map.Entry
 - Package getPackage():返回此类所属的包。
 - ClassLoader getClassLoader():返回该类的类加载器。
 - Class<? super T> getSuperclass():返回表示类的超类的 Class 对象。
 - Class<?>[] getInterfaces():返回类实现的所有接口。
 - boolean isInterface():判断是否是接口。
 - boolean isAnnotation():判断是否是注解类型。
 - boolean isEnum():判断是否是枚举类型。
 - Annotation[] getAnnotations():返回此元素上存在的所有注解。
 - Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。
 - T getAnnotation(Class
annotationClass):返回指定类型的注解,如果该注解存在于此元素上,否则返回 null。例如Spring源码中,ApplicaitonContext构造器判断一个类是不是Bean,是通过这个方法判断类有没有@Comonent等注解,从而判断它是不是Bean。  
 - 获取成员:
- Field[] getFields():返回类的所有公共字段,包括从父类继承的字段。
 - Field[] getDeclaredFields():返回类声明的所有字段,不包括继承的字段。
 - Method[] getMethods():返回类的所有公共方法,包括从父类继承的方法。
 - Method[] getDeclaredMethods():返回类声明的所有方法,不包括继承的方法。
 - Constructor<?>[] getConstructors():返回类的所有公共构造方法。
 - Constructor<?>[] getDeclaredConstructors():返回类声明的所有构造方法。
 
 - 其他方法:
- T newInstance():创建此 Class 对象所表示的类的一个新实例(使用默认构造方法)。
 
 
Spring源码:Bean初始化时判断类是否Bean、判断属性是否需要填充都用到了反射
1  | // 狗类  | 
全限定名和规范名
全限定名和规范名:
外部类的全限定名和规范名是一样的,都是“xxx.类名”。区别主要在内部类,内部类的全限定名是“xxx.外部类名$内部类名”,规范名是“xxx.外部类名.内部类名”。
- 简单名:只包含类名。例如Dog、String、Entry
 - 全限定名:包含包名和类名,用于唯一标识类或接口,通过全限定名能找到唯一一个类。例如package1.Dog、java.lang.String、java.util.Map$Entry
 - 规范名:类的规范字符串形式,常用于打印和日志记录。例如package1.Dog、java.lang.String、java.util.Map.Entry
 
1  | public static void main(String[] args) throws Exception {  | 
反射获取成员
反射获取构造方法
Class对象获取构造器:
- getConstructor(Class<?>… parameterTypes):获取指定参数类型的公共构造方法。返回值是Constructor类。
 - getDeclaredConstructor(Class<?>… parameterTypes):获取指定参数类型的构造方法(包括私有构造方法)。
 - getConstructors():获取所有公共构造方法。
 - getDeclaredConstructors():获取所有构造方法(包括私有构造方法)。
 - **newInstance()**:创建类的新实例。
- Class类的newInstance():只能够调用无参构造函数;
 - Constructor类的newInstance():可以根据传入的参数,调用任意构造函数。
 
 
1  | // 获取所有构造器对象:  | 
反射获取字段
Class对象获取字段:
- getField(String name):返回指定名称的公共字段。返回类型是字段类Field。
 - getDeclaredField(String name):返回指定名称的字段(包括私有字段)。
 - getFields():返回所有公共字段。
 - getDeclaredFields():返回所有字段(包括私有字段)。
 
字段类Field常用方法:
获取字段信息:
- getName():返回字段的名称。
 - getType():返回字段的类型。
 - getModifiers():返回字段的修饰符。
 - getDeclaringClass():返回声明该字段的类的 Class 对象。
 
获取和设置字段值:
- get(Object obj):返回指定对象上此字段的值。
 - getBoolean(Object obj):返回指定对象上此字段的值(如果字段类型是 boolean)。
 - getByte(Object obj):返回指定对象上此字段的值(如果字段类型是 byte)。
 - getChar(Object obj):返回指定对象上此字段的值(如果字段类型是 char)。
 - getDouble(Object obj):返回指定对象上此字段的值(如果字段类型是 double)。
 - getFloat(Object obj):返回指定对象上此字段的值(如果字段类型是 float)。
 - getInt(Object obj):返回指定对象上此字段的值(如果字段类型是 int)。
 - getLong(Object obj):返回指定对象上此字段的值(如果字段类型是 long)。
 - getShort(Object obj):返回指定对象上此字段的值(如果字段类型是 short)。
 - set(Object obj, Object value):设置指定对象上此字段的值。注意私有字段默认不允许赋值,要赋值必须给私有字段setAccessible(true)。
 - setBoolean(Object obj, boolean value):设置指定对象上此字段的值(如果字段类型是 boolean)。
 - setByte(Object obj, byte value):设置指定对象上此字段的值(如果字段类型是 byte)。
 - setChar(Object obj, char value):设置指定对象上此字段的值(如果字段类型是 char)。
 - setDouble(Object obj, double value):设置指定对象上此字段的值(如果字段类型是 double)。
 - setFloat(Object obj, float value):设置指定对象上此字段的值(如果字段类型是 float)。
 - setInt(Object obj, int value):设置指定对象上此字段的值(如果字段类型是 int)。
 - setLong(Object obj, long value):设置指定对象上此字段的值(如果字段类型是 long)。
 - setShort(Object obj, short value):设置指定对象上此字段的值(如果字段类型是 short)。
 
其他方法:
- isAccessible():返回字段是否可访问。
 - setAccessible(boolean flag):设置字段的可访问性。通过这个方法可以让私有字段也可以赋值。
 - oGenericString():返回字段的描述,包括泛型信息。
 - getAnnotatedType():返回此字段的带注释的类型。
 - getAnnotations():返回字段的所有注解。
 - getAnnotation(Class
annotationClass): 返回字段的指定类型的注解,如果该注解不存在,则返回 null。例如Spring源码中依赖注入这一块,就是基于反射获取类中字段有没有@Resource、@Component等注解,有的话就是要注入Bean. - getDeclaredAnnotations():返回直接存在于此字段上的所有注解。
 
1  | // 获取成员变量对象并赋值  | 
反射获取普通方法
Class对象获取成员方法的方法:
getMethod(String name, Class<?>… parameterTypes):返回指定名称和参数类型的公共方法。返回值是方法类Method。
getDeclaredMethod(String name, Class<?>… parameterTypes):返回指定名称和参数类型的方法(包括私有方法)。
getMethods():返回所有公共方法(包括从父类继承的方法)。
getDeclaredMethods():返回所有方法(包括私有方法)。
Method类的方法:
- 获取方法信息:
- getName():返回方法的名称。
 - getReturnType():返回方法的返回类型。
 - getParameterTypes():返回方法参数类型的数组。
 - getModifiers():返回方法的修饰符。
 - getDeclaringClass():返回声明此方法的类的 Class 对象。
 
 
- 获取方法信息:
 调用方法:
- Object invoke(Object obj, Object… args):调用指定对象上此 Method 对象表示的基础方法。
 
其他方法:
- isAccessible():返回方法是否可访问。
 - setAccessible(boolean flag):设置方法的可访问性。
 - getAnnotations():返回此方法的所有注解。例如Spring源码中通过此方法判断一个类中
 - isAnnotationPresent(Class<? extends Annotation> annotationClass):判断此方法是否被指定的注解类型注释。
 - getAnnotation(Class
annotationClass):返回该方法的指定类型的注解。  - getExceptionTypes():返回此方法抛出的异常类型的数组。
 - toGenericString():返回方法的描述,包括泛型信息。
 
获取成员变量对象并调用:
1  | // 1.获取构造方法对象并实例化  | 
相关问题
什么是 Java 序列化?什么情况下需要序列化?
Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。
以下情况需要使用 Java 序列化:
- 想把内存中的对象状态保存到一个文件中或者数据库中时候;
 - 想用套接字在网络上传送对象的时候;
 - 想通过RMI(远程方法调用)传输对象的时候。
 
动态代理是什么?有哪些应用?实现原理?
动态代理:运行时动态创建代理对象的技术,通过反射机制生成代理类,无需手动编写代理代码即可实现对目标对象的间接访问和控制。
动态代理应用:实现AOP、日志记录、权限校验、hibernate 数据查询、测试框架的后端 mock、rpc等功能
实现原理:包括JDK动态代理(基于接口)和CGLIB动态代理(基于类继承)。





