1.反射的作用及基本概念
1、反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息(比如成员变量、构造器、成员方法等等),并能操作对象的属性和方法,反射在设计模式和框架底层都会用到
2、加载完类后,在堆中就会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的内部结构,所以形象的称之为:反射
2.反射机制
1、反射可以在运行时判断一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行得到任意一个类所具有的成员变量和方法、可以调用一个对象的成员变量和方法
4、动态代理
3.java程序在计算机中的三个阶段
4.反射相关的类
1、java.lang.Class:代表一个类,Class对象表示某个类在加载后在堆中的对象
2、java.lang.reflect.Method:代表类的方法
3、java.lang.reflect.Constructor:代表类的构造方法
4、java.lang.reflect.Filed:代表类的成员变量
5.反射的优缺点
1、优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
2、缺点:使用反射基本是解释执行,对执行速度有影响
3、反射调优:
- Method和Field、Constructor对象都有setAccessible()方法开关
- setAccessible作用时启动和禁用访问检查安全的开关
- 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率,参数值为false则表示反射的对象执行访问检查
6.Class类
1、Class类也是类,因此也继承Object类
2、Class类对象不是new出来的,而是系统创建的
3、对于某个类的Class对象,在内存中只有一份,因为类只加载一次
4、每个类的实例都与自己的Class实例有关联
5、通过Class可以完整地得到一个类的完整结构,通过一系列API
6、Class对象是存放在堆中
7、类的字节码二进制数据,是放在方法区中的,也称为类的元数据
7.获取Class类对象
1、前提:已知一个类的全类名、且该类在类路径下,可通过Class类的静态方法forName()获取到,可能抛出ClassNotFoundException
实例:Class clazz = Class.forName(java.lang.Cat)
应用场景:多用于配置文件,读取类的全路径、加载类
2、前提:已知具体的类,通过类的Class来获取,该方式最为安全可靠、程序性能较高
实例:Class clazz = String.Class
应用场景:多用于参数传递,比如通过反射来获取对应构造器对象
3、前提:已知对象,通过对象.getClass()来获取Class对象
实例:Car car = new Car();
Class clazz = car.getClass();
4、通过类加载器来获取
package reflection;
import thread.BankTest;
public class GetClass_ {
public static void main(String[] args) {
try {
//1 Class.forName
Class<?> aClass = Class.forName("thread.BankTest");
System.out.println(aClass);
//2 类名.Class
Class<BankTest> bankTestClass = BankTest.class;
System.out.println(bankTestClass);
//3 对象.getClass
BankTest bankTest = new BankTest();
System.out.println(bankTest.getClass());
//4 通过类加载器来获得
//1) 先得到类加载器 car
ClassLoader classLoader = bankTest.getClass().getClassLoader();
//2) 通过类加载器获得Class对象
Class<?> loadClass = classLoader.loadClass("thread.BankTest");
System.out.println(loadClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
8.哪些类型有Class对象
1、外部类,所有的内部类
2、interface、接口
3、数组
4、Enum枚举类
5、annotation:注解
6、基本数据类型
7、void
9.类加载
1、静态加载:编译时加载相关的类、如果没有则报错,依赖性较强
2、动态加载:运行时加载需要的类、如果运行时不用该类,则不报错,降低了依赖性
类加载时机:
- 当创建对象时
- 当子类被加载时
- 调用类中的静态成员时
- 通过反射
.jpg)
.jpg)
.jpg)
.jpg)
准备阶段属性时如何处理的:
class A {
//属性-字段-成员变量
//类加载的准备阶段 属性的处理过程
//1. n1是实例属性,不是静态变量,因此在准备阶段,不会分配内存
//2. n2是类变量,分配内存并初始化为0,而不是20
//3. n3是类常量,分配内存,和常量不一样,因为一旦赋值就不变 n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}
在该阶段时,JVM会对静态变量、分配内存并默认初始化(对应数据类型的默认初始化值,null,0,false等)。这些变量所使用的内存都将在方法区中进行分配内存。
解析阶段:虚拟机将常量池内的符号引用替换为直接引用的过程
初始化阶段:
- 到初始化阶段,才开始真正的执行类中定义的java程序代码,此阶段时执行()方法的过程;
- ()方法由编译器按语句在源文件中出现的顺序,依此自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并;
- 虚拟机会保证一个类的()方法在多线程中正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕
10.通过反射获取类的结构信息
第一组:java.lang.Class类
1、getName():获取全类名
2、getSimpleName():获取简单类名
3、getFields():获取所有public修饰的属性,包含本类的和父类的
4、getDeclaredFields():获取本类中所有属性
5、getMethods():获取所有public修饰的方法,包含本类的和父类的
6、getDeclaredMethods():获取所有本类中的方法
7、getConstructors():获取所有public修饰的构造器,包含本类的
8、getDeclaredConstructors():获取本类所有的构造器
9、getPackage():获取包信息
10、getSuperClass():以Class形式返回父类信息
11、getInterfaces():以Class[]形式返回接口信息
12、getAnotations():以Annotation[]返回注解信息
第二组:java.lang.reflect.Field类
1、getModifiers:以int形式返回修饰符
默认修饰符:0;public是1;private是2;protected是4;static是8;final是16;
2、getType:以Class形式返回类型
3、getName:返回属性名
第三组:java.lang.reflect.Method类
1、getModifiers:以int形式返回修饰符
默认修饰符:0;public是1;private是2;protected是4;static是8;final是16;
2、getReturnType:以Class形式返回类型
3、getName:返回方法名
4、getParameterTypes:以Class[]形式返回参数类型数组
第四组:java.lang.reflect.Constructor类
1、getModifiers:以int形式返回修饰符
默认修饰符:0;public是1;private是2;
2、getParameterTypes:以Class形式返回参数类型
3、getName:返回构造器名(全类名)
11. 通过反射创建对象
1.方式一:调用类中的public修饰的无参构造器
2.方式二:调用类中的指定构造器
3.Class类的相关方法:
- newInstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class …clazz):根据参数列表,获取对应的public构造器对象
- getDecalaredConstructor(Class…clazz):根据参数列表,获取对应的所有的构造器对象
4.Constructor类相关方法:
- setAccessible:爆破
- newInstance(Object…obj):调用构造器
12.通过反射访问类中的成员
访问属性:
1.根据属性名获取field对象
Field f = class对象.getDelclaredField(属性名);
2.爆破:setAccessible(true);
3.访问:f.set(o,值),f.get(o)
4.如果是静态属性,则set和get中的参数o,可以写成Null
访问方法:
1.根据方法名和参数列表获取Method方法对象:Method m = clazz.getDeclaredMethod(方法名,XX.class)//得到本类的所有方法
2.获取对象:Object o = class.newInstance()
3.暴破:m.setAccessible(true)
4.访问:object returnValue = m.invoke(o,实参列表)
5.注意:如果是静态方法,则invoke的参数o,可以写成null