1 加载
在代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。
-
加载:
将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口。
-
链接:
将Java类的二进制代码合并到JVM的运行状态之中。
• 验证
确保加载的类信息符合JVM规范,没有安全方面的问题。
• 准备
正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。注意此时的设置初始值为默认值,具体赋值在初始化阶段完成。
• 解析
虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程。 -
初始化:
初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。
• 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先初始化其父类。
• 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
类的初始化顺序:
1、父类的静态变量
2、父类的静态代码块
3、子类的静态变量
4、子类的静态代码块
5、父类的非静态变量
6、父类的非静态代码块
7、父类的构造方法
8、子类的非静态变量
9、子类的非静态代码块
10、子类的构造方法
2 实例化
类的实例化,是指创建一个对象的过程。这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。
Spring什么时候实例化bean,首先要分2种情况
第一:如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化
第二:如果你使用ApplicationContext作为Spring Bean的工厂类,则又分为以下几种情况:
(1):如果bean的scope是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则 ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候, 直接从这个缓存中取
(2):如果bean的scope是singleton的,并且lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
(3):如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
Spring中Bean的实例化过程
Spring装配Bean的过程
- 实例化(调用构造方法);
- 设置属性值;
- 如果实现了BeanNameAware接口,调用setBeanName设置Bean的ID或者Name;
- 如果实现BeanFactoryAware接口,调用setBeanFactory 设置BeanFactory;
5.如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext - 调用BeanPostProcessor的预先初始化方法;
- 调用InitializingBean的afterPropertiesSet()方法;
- 调用定制init-method方法;
- 调用BeanPostProcessor的后初始化方法;
从大流程来说,类肯定是先初始化,再实例化的,这里得出第一个顺序:
静态域 --> 实例域 --> 构造函数。另外要符合任何子类的动作都会触发父类:父类 --> 子类。所以得出原则:【先静态后实例;先父类后子类】
而且同一个域的顺序可以分成两步: 创建-->赋值
对于静态域,其先经过链接创建静态变量,赋default值;再到初始化阶段给静态变量赋assign值和执行静态代码块。同理于实例域,也是分成创建和赋值两个部分,不同的只是加入构造函数(形参和代码块):先创建实例变量和构造函数形参以default值,然后对变量和形参赋assign值和执行实例代码块,最后执行构造函数的代码。