当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载
就是指将class文件读入内存,并为之创建一个Class对象。 任何类被使用时系统都会建立一个Class对象。
- 连接
验证:是否有正确的内部结构,并和其他类协调一致. 确保Class文件中包含的信息符合当前虚拟机的要求.比如,校验某非抽象类是否实现了抽象父类的全部方法.方法体中 类型转换 符号是否有效等.
准备:负责为类的静态成员分配内存,并设置默认初始化值.
解析:将类的二进制数据中的符号引用替换为直接引用.
- 初始化
对类的静态内容执行初始化操作
- 类的加载时机
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类
6. 直接使用java.exe命令来运行某个主类 main()
到目前为止我们已经知道把class文件加载到内存做了哪些事情了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?那就是我们反射将要学习的内容了。
- 反射概述
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
简而言之,反射就是:在运行时通过代码操作Class,从而可以获取类中的字段,构造,方法等
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖这个类,使用的就是Class类中的方法,所以先要获取到每一个字节码文件所对应的Class类型对象。
- Class的三种获取方式
//1 通过类型获得
// 语法:类名.class
// 应用场景:确定类型等
Class clazz1 = Bean.class;
//2 通过实例对象获得
// 语法:变量.getClass()
// 应用场景:在方法内部通过参数获得类型等
Bean bean = newBean();
Class clazz2 = bean.getClass();
//3 通过字符串获得
// 语法:Class.forName("全限定类名")
// 应用场景:通过配置获得字符串等
Class clazz3 = Class.forName("com.rf.Bean");
- 获取使用方法字段等
//1获得构造-- 没有形参
Constructor constructor = beanClass.getConstructor();
//2实例对象,没有实参
Object bean = constructor.newInstance();
//1获得构造-- 两个字符串形参 -- Bean(String id, String className)
Constructor constructor = beanClass.getConstructor(String.class,String.class);
//2 实例对象,两个字符串实参
Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList");
- 获取私有(暴力反射)
getConstructor() 使用该方法将无法获得私有方法,程序运行抛异常
私有的属性方法都需要设置bean.setAccessible(true);