介绍具体的例子
Parent类:
public class Parent {
public Parent() {
System.out.println("这是父类的构造方法");
}
public void d_Parent() {
System.out.println("这是父类的非静态方法");
}
public static void s_Parent() {
System.out.println("这是父类的静态方法");
}
static {
System.out.println("这是父类的静态代码快");
}
{
System.out.println("这是父类的构造代码快");
}
}
Child类:
public class Child extends Parent{
public Child() {
System.out.println("这是子类的构造方法");
}
public static void s_Child() {
System.out.println("这是子类的静态方法");
}
public void d_Child() {
System.out.println("这是子类的非静态方法");
}
static{
System.out.println("这是子类的静态代码块");
}
{
System.out.println("这是子类的构造代码块");
}
public static void main(String[] args) {
System.out.println("before new Child()");
new Child();
}
}
输出结果:
这是父类的静态代码块
这是子类的静态代码块
before new Child()
这是父类的构造代码块
这是父类的构造方块
这是子类的构造代码块
这是子类的构造方法
结论:
首先,静态代码块,构造代码块,和构造函数都存在于一个类中,只不过,他们执行的先后顺序和执行的次数不同。
静态代码块:用staitc声明,jvm加载类时执行,仅执行一次。
构造代码块:类中直接用{}定义,每一次创建对象时执行。
构造函数:构造函数的命名必须和类名完全相同,它没有返回值,也不能用void来修饰。
执行顺序优先级:静态块>main()>构造块>构造方法。
JVM分析
一、JVM加载类文件
将类文件中的一行行内容全部加载到内存中(除了实例变量,因为这是对象私有的),但不执行任何语句,即使在加载时期有输出语句也不执行。加载的时候,将静态成员变量(类变量),构造代码块,静态代码块以及静态方法加载到方法区的静态部分,非静态方法以及构造方法加载到方法区的非静态部分。
二、执行代码块
类文件加载好以后,开始执行静态代码块,然后在堆内存中开辟空间,分配内存地址。
接着开始在堆内存中对实例变量进行默认初始化,
然后执行构造代码块,对object进行对应的构造代码块的初始化,
最后进行构造函数的初始化,对object进行对应的构造函数初始化。
作用分析
静态代码块用于给类初始化,类加载的时候就会被执行;
构造代码块用于给对应对象初始化,只要创建对象就会被执行,而且执行的顺序优先于构造函数;
构造函数用于给对应对象初始化,只要创建对象,就会选择相应的构造函数进行初始化。
总结:静态代码块是最先执行的,然后执行父类的构造代码块以及父类的构造方法,接着去执行子类的非静态代码块以及子类的构造方法。
顺序为:初始化父类静态代码块->初始化子类静态代码块->初始化父类构造代码块->初始化父类构造方法->初始化子类构造代码块->初始化子类构造方法
根据输出结果,"before new Child()"是在是在执行完静态代码块以后然后才输出的,这就证明静态代码块是不需要创建对象就可以执行的,只要加载完类文件就可以执行,而构造代码块以及构造方法是在执行创建对象的时候才进行初始化的。
那么,什么时候才会加载类文件呢?
1.执行new Child() 操作的时候;
2.使用类中的静态成员变量或者静态方法的时候;
3.在命令行中执行:java Child的时候。