1.反射定义
** 1. java反射是指在程序运行状态中,任意获取某个类的结构,创建对象,得到方法,属性,注解等等信息,这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制(指程序可以访问、检测和修改它本身状态或行为的一种能力)**
2.反射可以实现的功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 在运行时处理注解
- 在运行时获取泛型信息
- 生成动态代理。
3.类加载器
定义及作用: Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。
java默认有三种类加载器,BootstrapClassLoader、ExtensionClassLoader、App ClassLoader。
- BootstrapClassLoader(引导启动类加载器):
嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,引导启动类加载器无法被应用程序直接使用。 - ExtensionClassLoader(扩展类加载器):
ExtensionClassLoader是用JAVA编写,且它的父类加载器是Bootstrap。是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类 库。它的父加载器是BootstrapClassLoader - App ClassLoader(应用类加载器):
App ClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文 件。它的父加载器为Ext ClassLoader
过程:
1. 加载:将编译后的class字节码文件加载到内存中,将类中的静态数据转换成方法区的运行数据结构,然后在堆中生成一个代表这个类的java.lang.class对象
2. 链接:将java类的二进制代码合并到JVM运行状态之中的过程
- 验证:确保加载类的信息符合JVM的规范,没有安全方面问题.
- 准备:正式为类变量(static)进行分配内存并初始化类变量默认值,内存都在方法区中分配
- 解析: 虚拟机常量池内的符号引用替换为直接引用(地址)的过程(我们知道所有的类都是引用类型,它会去找其真实的类型)
3. 初始化
- 执行类构造器()方法的过程,类构造器()方法是由编译期自动收集类所有变量的赋值动作和静态代码块中的语句合并产生的
- 若初始化的时候,发现父类没有初始化,则需要先触发父类的初始化
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁与同步
4. 补充
双亲委派机制介绍:
如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求 转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的 启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类) 时,子类加载器才会尝试自己去加载。
委派的好处就是避免有些类被重复加载,防止java的核心类被串改
5.类加载机制图
4.反射获取Class
要想了解一个类,必须先要获取到该类的字节码文件对象. 在Java中,每一个字节码文件,被加载在到内存后,都存在一个对应的Class类型的对象
获取到class的几种方式
- 如果在编写代码时, 知道类的名称, 且类已经存在, 可以通过
包名.类名.class 得到一个类的 类对象 - 如果拥有类的对象, 可以通过
Class 对象.getClass() 得到一个类的 类对象 - 如果在编写代码时, 知道类的名称 , 可以通过
Class.forName(包名+类名): 得到一个类的 类对象
5.获取反射类的信息
- 反射获取 Constructor
- 通过指定的参数类型, 获取指定的单个构造方法 getConstructor(参数类型的class对象数组)
例如:
构造方法如下:
Person(String name,int age)
得到这个构造方法的代码如下:
Constructor c = p.getClass().getConstructor(String.class,int.class);
-
获取构造方法数组
getConstructors(); -
获取所有权限的单个构造方法
getDeclaredConstructor(参数类型的class对象数组) -
获取所有权限的构造方法数组
getDeclaredConstructors();
setAccessible(boolean flag)
如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)
- 反射获取 Method
通过class对象 获取一个类的方法
-
getMethod(String methodName , class… clss)
根据参数列表的类型和方法名, 得到一个方法(public修饰的) -
getMethods();
得到一个类的所有方法 (public修饰的) -
getDeclaredMethod(String methodName , class… clss)
根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认) -
getDeclaredMethods();
得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
Method 执行方法
-
invoke(Object o,Object… para) :
调用方法 ,
参数1. 要调用方法的对象
参数2. 要传递的参数列表 -
getName()
获取方法的方法名称 -
setAccessible(boolean flag)
如果flag为true 则表示忽略访问权限检查 (可以访问任何权限的方法)
- 反射获取 Field
通过class对象 获取一个类的属性
- getDeclaredField(String filedName)
根据属性的名称, 获取一个属性对象 (所有属性) - getDeclaredFields()
获取所有属性 - getField(String filedName)
根据属性的名称, 获取一个属性对象 (public属性) - getFields()
获取所有属性 (public)
常用方法:
-
get(Object o);
参数: 要获取属性的对象 获取指定对象的此属性值 -
set(Object o , Object value);
参数1. 要设置属性值的 对象
参数2. 要设置的值 设置指定对象的属性的值 -
getName();
获取属性的名称 -
setAccessible(boolean flag);
如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)
- 通过反射获取注解信息
获取类/属性/方法的全部注解对象
Annotation[] annotations01 = Class/Field/Method.getAnnotations();
for (Annotation annotation : annotations01) {
System.out.println(annotation);
}
根据类型获取类/属性/方法的注解对象`这里是引用
注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);
8.反射优缺点
优点:
1. 可以实现动态创建对象和编译,提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
2. 代码简洁,可读性强,可提高代码的复用率
缺点:
1. 对性能有影响,使用反射基本是是一种解释操作,我们可以告诉JVM我们希望做什么,并且它能满足我们的要求,这类操作总是慢于直接执行相同的操作
2. 内部暴露和安全隐患(破坏单例)