(来自于B站韩老师Java反射专题整理相关笔记,链接:https://www.bilibili.com/video/BV1g84y1F7df)
ps:好像从博客园不能直接跳转,可以复制到浏览器打开
1. 反射机制
1.1 引出:
需求:根据配置文件(xx.properties)创建对象并调用方法
这种需求通过外部文件配置,在不修改源码的情况下,控制程序,符合设计模式的opc原则(开闭原则:不修改源码的情况下,扩展功能)
传统方法难以解决,引出反射机制
1.2 反射机制解决
// 1.加载类,返回Class类型的对象
Class clazz = Class.forName("全类名");
// 2.通过class获得加载类的对象实例
Object o = clazz.newInstance();
// 3.通过class获得加载类的方法对象
// 在反射中,可以将方法视为对象
Method method = clazz.getMethod("方法名");
// 4.通过方法对象来实现方法调用 方法.invoke(对象)
method.invoke(o);
1.3 概念
反射机制就是程序在运行期间借助Reflection API取得任何类的内部信息(成员变量,构造器,成员方法等),并且能够操作对象的属性和方法
类加载完以后,在堆中产生了一个Class类型的对象(一个类只有一个Class对象),该对象包含类的完整结构信息,通过这个对象得到类的结构,这个对象就像一面镜子,透过镜子可以看到类的结构,所以形象的称之为反射
1.4 原理
Java程序执行有三个阶段:
- 编译:javac将.java文件编译成字节码文件
- 加载:通过类加载器生成Class对象(体现反射)
- 运行:创建的对象(堆中)知道自己属于哪个Class对象
1.5 优缺点
优点:动态创建和使用对象,使用灵活
缺点:解释执行,对执行速度有影响
2. Class类
- 继承Object类
- Class类对象不是new的,而是系统创建的(ClassLoader.loadClass()),可以debug看到
- 对于某个类的Class对象,在内存(堆)中只会存在一份,因为类只会加载一次(可以创建两个比较HashCode,值一样)
- 每个类的实例知道自己属于哪个Class对象
- 通过Class对象可以得到类的完整结构
- Class对象存放在堆中
- 类的元数据(方法代码,变量名,方法名,访问权限等),即类的字节码二进制数据,存放在方法区
2.2 获取Class类对象
-
通过Class.forName()获取:已知类的全类名,且该类在类路径下,多用于配置文件,编译阶段
-
类名.class:多用于参数传递 String.class,加载阶段
-
对象.getClass():有对象实例,运行阶段
-
通过类加载器获取:
ClassLoader classloader = 对象.getClass().getClassLoader(); Class cls = classloader.loadClass("全类名");
-
基本数据类型.class:输出基本类型 int
-
包装类.TYPE:输出基本类型
3. 类加载
类加载的三个阶段:
- 加载:将class文件读入内存,创建Class对象,由类加载器完成
- 连接:将类的二进制数据合并到JRE中
- 验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全(比如文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证等),可以通过使用-Xverify:none关闭大部分的类验证,缩短虚拟机类加载时间
- 准备:对静态变量分配内存并默认初始化,这些变量所使用的内存在方法区中进行分配
- 解析:虚拟机将常量池中的符号引用替换为直接引用
- 初始化:JVM负责对类进行初始化,主要指静态成员,该阶段真正开始执行类中定义的Java程序代码,此阶段是执行
()方法的过程
()方法是由编译器按语句在文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并
虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,只会有一个线程去执行这个类的 ()方法,其他线程都要阻塞等待,直到活动线程执行 ()方法完毕
类加载后内存中:方法区中存放类的字节码二进制数据,堆中生成类的Class对象
4. 反射获取类的结构信息
Class类:
Field类:
如果public static则返回1+8=9
Method类:
Constructor类:
5. 反射相关类
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
clazz.getField("")方法不能得到私有属性
Class.getConstructor(形参类的class对象)
6. 反射调用性能优化
测试反射调用性能和优化方案:
对同样的类调用90000000次,采用传统方法和反射调用相差数百倍
优化:关闭访问检查
Method、Field、Constructor对象都有setAccessible()方法,可以开启关闭访问安全检查,参数值为true表示反射的对象在使用时取消访问检查,提高性能(优化一点点)
7. Class类常用方法
Class cls = Class.forName("")
System.out.println(cls);//com.xxx.xx 显示cls对象
System.out.println(cls.getClass());//java.lang.Class 运行类型
System.out.println(cls.getPackage().getName());//获取包名
cls.getName();//获取全类名
cls.newInstance();//创建对象实例
Field field = cls.getField("");//获取属性
field.set();//给属性赋值
Field[] fields = cls.getFields();//获取所有属性