类加载过程
(1)类加载机制
JVM把class文件加载到内存,然后对数据进行链接(验证,准备,解析),然后再进行初始化,然后再进行使用
加载------>链接------>初始化-------->使用
首先,jVM把class文件加载到内存。avac将类进行编译,形成class文件。然后类加载器将class文件内容加载到内存中,将静态数据转换成方法区中的运行数据,在堆中形成代表这个类的class对象,外部可以通过class对象操作类
在链接中,有验证,准备和解析三个步骤
1.验证:这是确保加载类的信息符合JVM规范,没有安全方面的问题
2.准备:为类变量(static变量)分配内存并设置初始值(这个初始值不是指的是代码里赋的初始值,而是真正意义上的初始值)
3.解析:常量池内的符号引用替换为直接引用(也就是地址)
常量池:存放一些符号引用,比如类名,方法名,变量名,类型名称,值
然后就进行初始化。初始化阶段执行类构造器< clint >()方法。类构造器< clint >()方法由编译器自动收集类中的所有变量的赋值动作和静态语句块中的语句。
当初始化一个类时,如果发现其父类还没有进行过初始化,则先初始化父类
类的初始化
执行类构造器< clint >()方法
类的引用分为主动引用和被动引用。主动引用一定会发生类的初始化,类的被动引用不会发生类的初始化
(1)主动引用
1.new对象时
public class Demo1 {
public static void main(String[] args) {
new Temp();
}
}
class Temp{
public static int a=1;
static {
System.out.println("静态初始化Temp");
a=11;
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化Temp
创建对象
可以看出先进行了初始化
2.调用类的静态成员和静态方法
public class Demo {
public static void main(String[] args) {
System.out.println(Temp.a);
}
}
class Temp{
public static int a=1;
static {
System.out.println("静态初始化Temp");
a=11;
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化Temp
11
可以看出进行了初始化
3.对类进行反射
package com.Test;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.Test.Temp");
}
}
class Temp{
public static int a=1;
static {
System.out.println("静态初始化Temp");
a=11;
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化Temp
4.当虚拟机启动
public class Demo {
static {
System.out.println("静态初始化main方法所在类");
}
public static void main(String[] args) throws ClassNotFoundException {
new Temp();
}
}
class Temp{
public static int a=1;
static {
System.out.println("静态初始化Temp");
a=11;
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化main方法所在类
静态初始化Temp
创建对象
可以看出先初始化了Demo类,也就是虚拟机啊启动了这个类(java Demo) ,也就是先启动main方法所在的类
5.先初始化父类
public class Demo1 {
static {
System.out.println("静态初始化main方法所在类");
}
public static void main(String[] args) throws ClassNotFoundException {
new Temp();
}
}
class Father{
static {
System.out.println("初始化Father");
}
}
class Temp extends Father{
public static int a=1;
static {
System.out.println("静态初始化Temp");
a=11;
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化main方法所在类
初始化Father
静态初始化Temp
创建对象
当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类
(2)被动引用
1.引用常量
public class Demo1 {
static {
System.out.println("静态初始化main方法所在类");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(Temp.a); //引用常量
}
}
class Father{
static {
System.out.println("初始化Father");
}
}
class Temp extends Father{
public static final int a=1;
static {
System.out.println("静态初始化");
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化main方法所在类
1
可以看出Temp类没有被初始化,所以它的父类也没有被初始化
2.通过数组定义类引用
public class Demo1 {
static {
System.out.println("静态初始化main方法所在类");
}
public static void main(String[] args) throws ClassNotFoundException {
Temp[] t=new Temp[10]; //数组引用
}
}
class Father{
static {
System.out.println("初始化Father");
}
}
class Temp extends Father{
public static int a=1;
static {
System.out.println("静态初始化");
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化main方法所在类
可以看出Temp没有被初始化
3.访问静态域
当访问一个静态域时,只有真正声明这个域的类才会被初始化
如,通过子类引用父类的静态变量,不会导致子类初始化
public class Demo1 {
static {
System.out.println("静态初始化main方法所在类");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(Temp.b); //访问Father中的b
}
}
class Father{
public static int b=1;
static {
System.out.println("初始化Father");
}
}
class Temp extends Father{
public static final int a=1;
static {
System.out.println("静态初始化");
}
public Temp(){
System.out.println("创建对象");
}
}
输出结果:
静态初始化main方法所在类
初始化Father
1
可以看出子类并没有被初始化
初始化阶段是执行变量的赋值和静态块中的内容