一、什么时候加载和初始化
1、类什么时候加载
类的加载是通过类加载器(Classloader)完成的,它既可以是饿汉式[eagerly load](只要有其它类引用了它就加载)加载类,也可以是懒加载[lazy load](等到类初始化发生的时候才加载)。不过我相信这跟不同的JVM实现有关,然而他又是受JLS保证的(当有静态初始化需求的时候才被加载)
2、类什么时候初始化
加载完类后,类的初始化就会发生,意味着它会初始化所有类静态成员,以下情况一个类被初始化:
- 实例通过使用new()关键字创建或者使用class.forName()反射,但它有可能导致ClassNotFoundException。
- 类的静态方法被调用
- 类的静态域被赋值
- 静态域被访问,而且它不是常量
- 在顶层类中执行assert语句
二、加载和实例化对象
类加载过程:
1, JVM会先去方法区中找有没有相应类的.class存在。如果有,就直接使用;如果没有,则把相关类的.class加载到方法区
2, 在.class加载到方法区时,会分为两部分加载:先加载非静态内容,再加载静态内容
3, 加载非静态内容:把.class中的所有非静态内容加载到方法区下的非静态区域内
4, 加载静态内容:
4.1、把.class中的所有静态内容加载到方法区下的静态区域内
4.2、静态内容加载完成之后,对所有的静态变量进行默认初始化
4.3、所有的静态变量默认初始化完成之后,再进行显式初始化
4.4、当静态区域下的所有静态变量显式初始化完后,执行静态代码块
5,当静态区域下的静态代码块,执行完之后,整个类的加载就完成了。
6,如果存在继承关系,则父类先加载,再加载子类。
对象创建过程:
1, 在堆内存中开辟一块空间
2, 给开辟空间分配一个地址
3, 把对象的所有非静态成员加载到所开辟的空间下
4, 所有的非静态成员加载完成之后,对所有非静态成员变量进行默认初始化
5, 所有非静态成员变量默认初始化完成之后,调用构造函数
6, 在构造函数入栈执行时,分为两部分:先执行构造函数中的隐式三步,再执行构造函数中书写的代码
6.1、隐式三步:
1,执行super语句
2,对开辟空间下的所有非静态成员变量进行显式初始化
3,执行构造代码块
6.2、在隐式三步执行完之后,执行构造函数中书写的代码
7,在整个构造函数执行完并弹栈后,把空间分配的地址赋值给一个引用对象
总结类中能书写的成员
栈: 用来运行函数。可以存储局部信息
特点:先进后出
堆:用来存储实体(被new创建出来的)。
特点:
1, 每一个实体所开辟的空间都有一个地址
2, 每一个实体中存储的数据都有一个默认初始值
方法区:存放class和static。 方法区其实是由N多个小的区域构成。有存放非静态内容的非静态区域,还有存放静态内容的静态区域,还有存放常量的常量池 等
总结
1、加载过程:
先加载非静态区域(未实例化),再加载静态区域默认初始化、显示初始化,然后执行静态代码块。父类先加载。(这个过程看不到摸不着,感觉不到,平时也不需要理会这方面的问题,所以不多说)
2、对象创建过程:
对所有非静态成员变量进行默认初始化,调用构造函数,而构造函数分三步走,1、执行super语句,2、对所有非静态成员变量进行显式初始化,3、执行构造代码块。
所以在执行构造函数的代码块之前所有成员变量都已经经过显示初始化。在整个构造函数执行完并弹栈后,把空间分配的地址赋值给一个引用对象。
可见过程:
一、无继承关系
1、初始化静态成员变量。2、执行静态代码块。3、初始化非静态成员变量。4调用构造函数。5、把空间分配的地址赋值给一个引用对象。
二、存在继承关系
1、初始化父类静态成员变量。2、执行父类静态代码块。3、初始化子类静态成员变量。4、执行子类静态代码块。5、初始化父类非静态成员变量。6、执行父类非静态代码块。7、执行父类构造函数。8、初始化子类非静态成员变量。8、执行子类非静态代码块。9、执行子类构造函数。10、把空间分配的地址赋值给一个引用对象。
创建一个有继承关系的子类对象流程的简洁总结:先加载父类,再加载子类,实例化父类,最后实例化子类